A Raycast Extension to Search My Blog

A screen shot of Raycast running my new extension

I’ve been looking for a way to search through the local copy of my blog using Raycast.

I ended up writing a custom extension to do it. ChatGPT helped grease the way—especially in rendering the results.

It uses a brute force grep over the files’ contents which works fine given the size of the repository.

The two main actions on the extension are opening the post in my editor and copying a Markdown link to the post1.

Here is a look at the Raycast command:

export default function Command() {
  const [query, setQuery] = useState<string>("");
  const [results, setResults] = useState<SearchResult[]>([]);

  useEffect(() => {
    if (query.trim() === "") {
      setResults([]);
      return;
    }
    try {
      const posts = getAllPosts(BASE_PATH, BLOG_SUBDIRS);
      const matches = searchPosts(posts, query);
      setResults(matches);
    } catch (err) {
      console.error("Error reading blog posts:", err);
    }
  }, [query]);

  return (
    <List onSearchTextChange={setQuery} throttle isShowingDetail>
      {results.map(({ file, snippet }) => {
        const filename = path.basename(file);
        const relativePath = path.relative(BASE_PATH, file).replace(/\\/g, "/");

        // Convert to URL relative from site root based upon Hugo URL config
        const relativeUrl = `/${relativePath.replace(/\.md$/, "").replace(/\/\d\d\d\d-/, "/")}`;

        // Grab the title from the front matter
        const fileContent = fs.readFileSync(file, "utf8");
        const titleMatch = fileContent.match(/^title:\s*(.*)$/m);
        const title = titleMatch ? titleMatch[1].replace(/^['"]|['"]$/g, "") : filename;
        const markdownLink = `[${title}](${relativeUrl})`;

        return (
          <List.Item
            key={file}
            title={snippet.replace(/\*\*/g, "")}
            detail={<List.Item.Detail markdown={`**${relativeUrl}**\n\n---\n\n${snippet}`} />}
            actions={
              <ActionPanel>
                <Action.Open title="Open in VS Code" target={file} application="/Applications/Visual Studio Code.app" />
                <Action.CopyToClipboard title="Copy Markdown Link" content={markdownLink} />
              </ActionPanel>
            }
          />
        );
      })}
    </List>
  );
}

And here is the full file.


  1. Which is handy when cross linking while writing other posts. ↩︎