Repository: adobe/react-spectrum
File: packages/dev/s2-docs/src/searchUtils.tsx (TypeScript)

Code before:
/**
 * Strips markdown link syntax from a string, keeping only the link text.
 */
export function stripMarkdown(description: string | undefined): string {
  return (description || '').replace(/\[(.*?)\]\(.*?\)/g, '$1');
}

/**
 * Gets all of the subheadings underneath a heading within the Table of Contents.
 */
function getToCSubheadings(TocNode: TocNode, headings: string[]): string[] {
  headings.push(TocNode.title);

  for (let node of TocNode.children) {
    getToCSubheadings(node, headings);
  }

  return headings;
}

/**
 * Transforms a page into a ComponentItem for search/display.
 */
export function transformPageToComponentItem(page: Page): ComponentItem {
  // get all headings on a page and add them a tags for the search feature
  let filterTags = new Set(['Content', 'Examples', 'API', 'Accessibility', 'Events', 'Features', 'Introduction', 'Interface']);
  let Toc = page.tableOfContents;
  let headings: string[] = [];
  if (Toc) {
    for (let node of Toc) {
      let subHeadings: string[] = getToCSubheadings(node, []);
      headings.push(...subHeadings);
    }
  }
  let allTags = (page.exports?.tags || page.exports?.keywords as string[]) || [];
  let relatedPages = (page.exports?.relatedPages?.map(page => page.title)) || [];
  allTags.push(...headings, ...relatedPages);
  allTags = allTags.filter(tags => (!filterTags.has(tags) && !tags.startsWith('Testing')));

  const title = getPageTitle(page);
  const section: string = getSearchSection(page);
  const tags: string[] = allTags;
  const description: string = stripMarkdown(page.exports?.description);
  const date: string | undefined = page.exports?.date;
  return {
    id: page.url,
    name: title,
    href: page.url,
    section,
    tags,
    description,

Review comment (suggestion):
One more: `'Example'`, sorry should have caught that.

```suggestion
  let filterTags = new Set(['Content', 'Example', 'Examples', 'API', 'Accessibility', 'Events', 'Features', 'Introduction', 'Interface']);
```

Code after:
  }
  
  return [r, g, b];
}

/**
 * Checks if a search string looks like a hex color code.
 */
function isHexColorSearch(searchValue: string): boolean {
  const trimmed = searchValue.trim();
  // Must start with # or be a valid 3 or 6 character hex string
  if (trimmed.startsWith('#')) {
    const hex = trimmed.slice(1);
    return /^[0-9A-Fa-f]{3}$/.test(hex) || /^[0-9A-Fa-f]{6}$/.test(hex);
  }
  // Also match without # if it's exactly 3 or 6 hex characters
  return /^[0-9A-Fa-f]{6}$/.test(trimmed);
}

/**
 * Calculates the color distance between two RGB colors using weighted Euclidean distance.
 * Uses weights that better approximate human color perception.
 * Reference: https://en.wikipedia.org/wiki/Color_difference.
 */
function colorDistance(rgb1: [number, number, number], rgb2: [number, number, number]): number {
  const [r1, g1, b1] = rgb1;
  const [r2, g2, b2] = rgb2;
  const rMean = (r1 + r2) / 2;
  const dr = r1 - r2;
  const dg = g1 - g2;
  const db = b1 - b2;
  const weightR = 2 + rMean / 256;
  const weightG = 4;
  const weightB = 2 + (255 - rMean) / 256;
  
  return Math.sqrt(weightR * dr * dr + weightG * dg * dg + weightB * db * db);
}

/**
 * Finds the closest colors to a given hex code from the color map.
 * Returns an array of color names sorted by distance (closest first).
 */
function findClosestColors(
  targetRgb: [number, number, number], 
  colorHexMap: Record<string, [number, number, number]>,
  maxResults = 10
): Array<{name: string, distance: number}> {
  const results: Array<{name: string, distance: number}> = [];
  
  for (const [name, rgb] of Object.entries(colorHexMap)) {
    const distance = colorDistance(targetRgb, rgb as [number, number, number]);