import { FilterList } from "./filter.tsx"; import { FilterOption } from "../types.ts"; import { CompletionContext, CompletionResult } from "../deps.ts"; import { PageMeta } from "$sb/types.ts"; import { isFederationPath } from "$sb/lib/resolve.ts"; const tagRegex = /#[^#\d\s\[\]]+\w+/g; export function PageNavigator({ allPages, onNavigate, completer, vimMode, darkMode, currentPage, }: { allPages: PageMeta[]; vimMode: boolean; darkMode: boolean; onNavigate: (page: string | undefined) => void; completer: (context: CompletionContext) => Promise; currentPage?: string; }) { const options: FilterOption[] = []; for (const pageMeta of allPages) { // Sanitize the page name if (!pageMeta.name) { pageMeta.name = pageMeta.ref; } // Order by last modified date in descending order let orderId = -new Date(pageMeta.lastModified).getTime(); // Unless it was opened in this session if (pageMeta.lastOpened) { orderId = -pageMeta.lastOpened; } // Or it's the currently open page if (currentPage && currentPage === pageMeta.name) { // ... then we put it all the way to the end orderId = Infinity; } // And deprioritize federated pages too if (isFederationPath(pageMeta.name)) { orderId = Math.round(orderId / 10); // Just 10x lower the timestamp to push them down, should work } let description: string | undefined; let aliases: string[] = []; if (pageMeta.displayName) { aliases.push(pageMeta.displayName); } if (Array.isArray(pageMeta.aliases)) { aliases = aliases.concat(pageMeta.aliases); } if (aliases.length > 0) { description = "(a.k.a. " + aliases.join(", ") + ") "; } if (pageMeta.tags.length > 1) { // Every page has the "page" tag, so it only gets interesting beyond that const interestingTags = pageMeta.tags.filter((tag) => tag !== "page"); description = (description || "") + interestingTags.map((tag) => `#${tag}`).join(" "); } options.push({ ...pageMeta, description, orderId: orderId, }); } let completePrefix = currentPage + "/"; if (currentPage && currentPage.includes("/")) { const pieces = currentPage.split("/"); completePrefix = pieces.slice(0, pieces.length - 1).join("/") + "/"; } else if (currentPage && currentPage.includes(" ")) { completePrefix = currentPage.split(" ")[0] + " "; } return ( { phrase = phrase.replaceAll(tagRegex, "").trim(); return phrase; }} preFilter={(options, phrase) => { const allTags = phrase.match(tagRegex); if (allTags) { // Search phrase contains hash tags, let's pre-filter the results based on this const filterTags = allTags.map((t) => t.slice(1)); options = options.filter((pageMeta) => { if (!pageMeta.tags) { return false; } return filterTags.every((tag) => pageMeta.tags.find((itemTag: string) => itemTag.startsWith(tag)) ); }); } return options; }} allowNew={true} helpText="Press Enter to open the selected page, or Shift-Enter to create a new page with this exact name." newHint="Create page" completePrefix={completePrefix} onSelect={(opt) => { onNavigate(opt?.name); }} /> ); }