diff --git a/.changeset/purple-bugs-relate.md b/.changeset/purple-bugs-relate.md
new file mode 100644
index 00000000..f32cf46c
--- /dev/null
+++ b/.changeset/purple-bugs-relate.md
@@ -0,0 +1,5 @@
+---
+'@myst-theme/site': patch
+---
+
+Replace token-length limit with character-length limit, hide ellipsis from markup
diff --git a/packages/site/src/components/Navigation/Search.tsx b/packages/site/src/components/Navigation/Search.tsx
index dba7c498..5dbb876e 100644
--- a/packages/site/src/components/Navigation/Search.tsx
+++ b/packages/site/src/components/Navigation/Search.tsx
@@ -1,4 +1,12 @@
-import { useEffect, useState, useMemo, useCallback, useRef, forwardRef } from 'react';
+import {
+ createElement,
+ useEffect,
+ useState,
+ useMemo,
+ useCallback,
+ useRef,
+ forwardRef,
+} from 'react';
import type { KeyboardEventHandler, Dispatch, SetStateAction, FormEvent, MouseEvent } from 'react';
import { useFetcher } from '@remix-run/react';
import {
@@ -42,10 +50,21 @@ function matchAll(text: string, pattern: RegExp) {
* Highlight a text string with an array of match words
*
* @param text - text to highlight
- * @param result - search result to use for highlighting
- * @param limit - limit to the number of tokens after first match
+ * @param matches - regular expression patterns to match against
+ * @param limit - limit to the number of characters after first match
+ * @param classname - CSS classname to use
*/
-function MarkedText({ text, matches, limit }: { text: string; matches: string[]; limit?: number }) {
+function MarkedText({
+ text,
+ matches,
+ limit,
+ className,
+}: {
+ text: string;
+ matches: string[];
+ limit?: number;
+ className?: string;
+}) {
// Split by delimeter, but _keep_ delimeter!
const splits = matchAll(text, SPACE_OR_PUNCTUATION);
const tokens: string[] = [];
@@ -79,23 +98,38 @@ function MarkedText({ text, matches, limit }: { text: string; matches: string[];
lastIndex = tokens.length;
} else {
firstIndex = tokens.findIndex((token) => pattern.test(token));
- lastIndex = firstIndex + limit;
+ let numChars = 0;
+ for (
+ lastIndex = firstIndex + 1;
+ lastIndex < tokens.length - 1 && numChars + tokens[lastIndex].length <= limit;
+ lastIndex++
+ ) {
+ numChars += tokens[lastIndex].length;
+ }
}
if (tokens.length === 0) {
- return <>{...tokens}>;
+ return {...tokens};
} else {
const firstRenderer = renderToken(tokens[firstIndex]);
const remainingTokens = tokens.slice(firstIndex + 1, lastIndex);
const remainingRenderers = remainingTokens.map((token) => renderToken(token));
return (
- <>
- {hasLimit && '... '}
+
{firstRenderer}
{...remainingRenderers}
- {hasLimit && ' ...'}
- >
+
);
}
}
@@ -178,8 +212,10 @@ function SearchShortcut() {
function SearchResultItem({
result,
closeSearch,
+ charLimit,
}: {
result: RankedSearchResult;
+ charLimit?: number;
closeSearch?: () => void;
}) {
const { hierarchy, type, url, queries } = result;
@@ -187,14 +223,13 @@ function SearchResultItem({
const Link = useLinkProvider();
// Render the icon
- const iconRenderer =
- type === 'lvl1' ? (
-