UNPKG

starlight-view-modes

Version:

Starlight plugin adding view mode capabilities to your documentation website.

145 lines (126 loc) 4.7 kB
import picomatch from "picomatch"; import astroConfig from "virtual:starlight-view-modes-context"; import type { StarlightViewModesConfig } from "./config"; import { AvailableModes } from "./definitions"; import { getLocaleFromSlug } from "./i18n"; import { ensureTrailingSlash, insertSegment, stripLeadingSlash, stripTrailingSlash, } from "./path"; export function getClassNameZenMode( displayOptions: StarlightViewModesConfig["zenModeSettings"]["displayOptions"] ): string { const parts: string[] = []; if (displayOptions.showHeader) parts.push("header"); if (displayOptions.showSidebar) parts.push("sidebar"); if (displayOptions.showTableOfContents) parts.push("table-of-contents"); if (displayOptions.showFooter) parts.push("footer"); return parts.length ? `starlight-view-modes-zen-mode-${parts.join("-")}` : "starlight-view-modes-zen-mode"; } /** * Check if the current URL pathname should be excluded from view mode switching * @param path The current URL pathname to check * @param exclude An array of glob patterns to exclude * @returns Whether the current URL pathname should be excluded */ export function isExcludedPage(path: string, exclude: string[]): boolean { return picomatch(exclude)(path); } /** * Filter out index slugs by returning undefined if the slug is an index page * @param slug The current URL pathname * @returns Undefined if the slug is an index page, otherwise the slug */ export function handleIndexSlug(slug: string): string | undefined { if (slug === "") return undefined; if (["index", "/"].some((indexSlug) => slug.startsWith(indexSlug))) { return undefined; } return slug; } export function handleAstroTrailingSlash(path: string): string { if (astroConfig.trailingSlash === "always") return ensureTrailingSlash(path); if (astroConfig.trailingSlash === "never") return stripTrailingSlash(path); return path; } /** * Insert a mode to a pathname, respecting the base path configuration and i18n. * If the mode is 'default', the pathname is returned unchanged. * * @param {string} pathname - The pathname to append the mode to including the base and locale * @param {string} mode - The mode to append (e.g., 'zen-mode', 'default') * @returns {string} - The pathname with the mode correctly inserted */ export function insertModePathname(pathname: string, mode: string) { // If the mode is default, return the pathname unchanged if (mode === "default") { return pathname; } const base = astroConfig?.base || ""; const locale = getLocaleFromSlug(pathname); const insertionPosition = base.split("/").filter(Boolean).length + (locale ? 1 : 0); return insertSegment(pathname, mode, insertionPosition); } /** * Get the active view mode * @param pathname The current URL path * @returns The active view mode as a string; "default" if no mode is active */ export async function getCurrentModeFromPath( pathname: string ): Promise<string> { let slug = stripLeadingSlash(stripTrailingSlash(pathname)); const base = stripLeadingSlash(stripTrailingSlash(astroConfig.base)); const locale = getLocaleFromSlug(slug); if (base) { slug = slug.startsWith(`${base}/`) ? slug.slice(base.length + 1) : slug.startsWith(`${base}`) ? slug.slice(base.length) : slug; } if (locale) { slug = slug.startsWith(`${locale}/`) ? slug.slice(locale.length + 1) : slug.startsWith(`${locale}`) ? slug.slice(locale.length) : slug; } return ( AvailableModes.find((mode) => slug.startsWith(`${mode.name}`))?.name || "default" ); } /** * Get the target URL path based on the current URL path and the target mode * @param pathname The current URL path * @param targetMode The mode you want to switch to * @returns The updated URL path */ export async function getUpdatedModePathname( pathname: string, targetMode: string ): Promise<string> { targetMode = stripLeadingSlash(stripTrailingSlash(targetMode)); const currentMode = await getCurrentModeFromPath(pathname); if (currentMode === targetMode) return pathname; // If switching back to "default", remove the current mode if (targetMode === "default") { return pathname.includes(`${currentMode}/`) ? pathname.replace(`${currentMode}/`, "") : pathname.replace(`${currentMode}`, ""); } // If currently in a mode, remove it before inserting the new mode let cleanedPath = pathname; if (currentMode !== "default") { cleanedPath = pathname.includes("/") ? pathname.replace(`${currentMode}/`, "") : pathname.replace(`${currentMode}`, ""); } return insertModePathname(cleanedPath, targetMode); }