@tenphi/renderer 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/dist/client/appearance.d.ts +12 -0
  4. package/dist/client/appearance.d.ts.map +1 -0
  5. package/dist/client/appearance.js +66 -0
  6. package/dist/client/appearance.js.map +1 -0
  7. package/dist/client/code-copy.d.ts +1 -0
  8. package/dist/client/code-copy.js +63 -0
  9. package/dist/client/code-copy.js.map +1 -0
  10. package/dist/client/heading-links.d.ts +6 -0
  11. package/dist/client/heading-links.d.ts.map +1 -0
  12. package/dist/client/heading-links.js +87 -0
  13. package/dist/client/heading-links.js.map +1 -0
  14. package/dist/client/mobile-toc.d.ts +1 -0
  15. package/dist/client/mobile-toc.js +28 -0
  16. package/dist/client/mobile-toc.js.map +1 -0
  17. package/dist/client/navigation.d.ts +1 -0
  18. package/dist/client/navigation.js +112 -0
  19. package/dist/client/navigation.js.map +1 -0
  20. package/dist/client/page-copy.d.ts +1 -0
  21. package/dist/client/page-copy.js +34 -0
  22. package/dist/client/page-copy.js.map +1 -0
  23. package/dist/client/search.d.ts +13 -0
  24. package/dist/client/search.d.ts.map +1 -0
  25. package/dist/client/search.js +87 -0
  26. package/dist/client/search.js.map +1 -0
  27. package/dist/client/sidebar.d.ts +1 -0
  28. package/dist/client/sidebar.js +93 -0
  29. package/dist/client/sidebar.js.map +1 -0
  30. package/dist/client/tabs.d.ts +6 -0
  31. package/dist/client/tabs.d.ts.map +1 -0
  32. package/dist/client/tabs.js +64 -0
  33. package/dist/client/tabs.js.map +1 -0
  34. package/dist/components/Callout.astro +15 -0
  35. package/dist/components/Card.astro +13 -0
  36. package/dist/components/CodeGroup.astro +47 -0
  37. package/dist/components/ContrastIcon.astro +29 -0
  38. package/dist/components/EditLink.astro +11 -0
  39. package/dist/components/GlobalStyles.js +1675 -0
  40. package/dist/components/HeaderLinks.astro +84 -0
  41. package/dist/components/Icon.astro +39 -0
  42. package/dist/components/LastUpdated.astro +17 -0
  43. package/dist/components/LayoutComponents.js +866 -0
  44. package/dist/components/Logo.astro +37 -0
  45. package/dist/components/MobileNavigationTabs.astro +79 -0
  46. package/dist/components/MobileTocList.astro +22 -0
  47. package/dist/components/PackageVersion.astro +25 -0
  48. package/dist/components/PageActions.astro +29 -0
  49. package/dist/components/Pagination.astro +29 -0
  50. package/dist/components/Preview.astro +34 -0
  51. package/dist/components/SidebarList.astro +122 -0
  52. package/dist/components/SiteLogo.astro +40 -0
  53. package/dist/components/SiteTitle.astro +7 -0
  54. package/dist/components/Steps.astro +6 -0
  55. package/dist/components/Tab.astro +10 -0
  56. package/dist/components/TableOfContents.astro +23 -0
  57. package/dist/components/Tabs.astro +16 -0
  58. package/dist/components/TastyComponents.js +255 -0
  59. package/dist/components/VersionSwitcher.astro +44 -0
  60. package/dist/components/appearance-preferences.test.ts +91 -0
  61. package/dist/components/appearance-preferences.ts +57 -0
  62. package/dist/components/component-styles.test.ts +129 -0
  63. package/dist/components/component-styles.ts +65 -0
  64. package/dist/components/customize-component.js +8 -0
  65. package/dist/components/props.d.ts +38 -0
  66. package/dist/components/props.d.ts.map +1 -0
  67. package/dist/components/props.js +1 -0
  68. package/dist/components/props.ts +30 -0
  69. package/dist/components/svg-icon.test.ts +11 -0
  70. package/dist/components/svg-icon.ts +11 -0
  71. package/dist/components/tasty-states.d.ts +2 -0
  72. package/dist/components/tasty-states.js +42 -0
  73. package/dist/components-public.d.ts +21 -0
  74. package/dist/components.d.ts +10 -0
  75. package/dist/components.js +9 -0
  76. package/dist/content.d.ts +11 -0
  77. package/dist/content.d.ts.map +1 -0
  78. package/dist/content.js +16 -0
  79. package/dist/content.js.map +1 -0
  80. package/dist/eslint-plugin.d.ts +32 -0
  81. package/dist/eslint-plugin.d.ts.map +1 -0
  82. package/dist/eslint-plugin.js +155 -0
  83. package/dist/eslint-plugin.js.map +1 -0
  84. package/dist/icons/arrow-left.svg +1 -0
  85. package/dist/icons/arrow-right.svg +1 -0
  86. package/dist/icons/check.svg +1 -0
  87. package/dist/icons/chevron-down.svg +1 -0
  88. package/dist/icons/chevron-right.svg +1 -0
  89. package/dist/icons/close.svg +1 -0
  90. package/dist/icons/copy.svg +1 -0
  91. package/dist/icons/device-desktop.svg +1 -0
  92. package/dist/icons/favicon.svg +11 -0
  93. package/dist/icons/menu.svg +1 -0
  94. package/dist/icons/moon.svg +1 -0
  95. package/dist/icons/palette.svg +1 -0
  96. package/dist/icons/search.svg +1 -0
  97. package/dist/icons/sun.svg +1 -0
  98. package/dist/index-BuX_Sb5c.d.ts +10 -0
  99. package/dist/index-BuX_Sb5c.d.ts.map +1 -0
  100. package/dist/index.d.ts +23 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +1648 -0
  103. package/dist/index.js.map +1 -0
  104. package/dist/localization.d.ts +69 -0
  105. package/dist/localization.d.ts.map +1 -0
  106. package/dist/localization.js +195 -0
  107. package/dist/localization.js.map +1 -0
  108. package/dist/markdown/rendered-content.d.ts +6 -0
  109. package/dist/markdown/rendered-content.d.ts.map +1 -0
  110. package/dist/markdown/rendered-content.js +34 -0
  111. package/dist/markdown/rendered-content.js.map +1 -0
  112. package/dist/messages-B_3xUzUb.js +14 -0
  113. package/dist/messages-B_3xUzUb.js.map +1 -0
  114. package/dist/navigation.d.ts +31 -0
  115. package/dist/navigation.d.ts.map +1 -0
  116. package/dist/navigation.js +165 -0
  117. package/dist/navigation.js.map +1 -0
  118. package/dist/overrides/DraftContentNotice.astro +8 -0
  119. package/dist/overrides/EmptyFooter.astro +3 -0
  120. package/dist/overrides/Footer.astro +23 -0
  121. package/dist/overrides/Head.astro +59 -0
  122. package/dist/overrides/Header.astro +139 -0
  123. package/dist/overrides/Hero.astro +73 -0
  124. package/dist/overrides/LanguageSelect.astro +63 -0
  125. package/dist/overrides/MarkdownContent.astro +14 -0
  126. package/dist/overrides/MobileMenuFooter.astro +15 -0
  127. package/dist/overrides/MobileMenuToggle.astro +36 -0
  128. package/dist/overrides/MobileTableOfContents.astro +37 -0
  129. package/dist/overrides/PageFrame.astro +35 -0
  130. package/dist/overrides/Search.astro +64 -0
  131. package/dist/overrides/Sidebar.astro +89 -0
  132. package/dist/overrides/SocialIcons.astro +27 -0
  133. package/dist/overrides/ThemeSelect.astro +148 -0
  134. package/dist/page-metadata.d.ts +13 -0
  135. package/dist/page-metadata.d.ts.map +1 -0
  136. package/dist/page-metadata.js +109 -0
  137. package/dist/page-metadata.js.map +1 -0
  138. package/dist/route-context.d.ts +85 -0
  139. package/dist/route-context.d.ts.map +1 -0
  140. package/dist/route-context.js +116 -0
  141. package/dist/route-context.js.map +1 -0
  142. package/dist/routes/DocsPage.astro +91 -0
  143. package/dist/routes/NotFound.astro +12 -0
  144. package/dist/routes/Page.astro +120 -0
  145. package/dist/styling.d.ts +17 -0
  146. package/dist/styling.d.ts.map +1 -0
  147. package/dist/styling.js +17 -0
  148. package/dist/styling.js.map +1 -0
  149. package/dist/tasty-states-BDKIbfw2.js +65 -0
  150. package/dist/tasty-states-BDKIbfw2.js.map +1 -0
  151. package/dist/theme/defaults.d.ts +21 -0
  152. package/dist/theme/defaults.d.ts.map +1 -0
  153. package/dist/theme/defaults.js +93 -0
  154. package/dist/theme/defaults.js.map +1 -0
  155. package/dist/theme/fonts.ts +64 -0
  156. package/dist/theme/shiki-theme.d.ts +13 -0
  157. package/dist/theme/shiki-theme.d.ts.map +1 -0
  158. package/dist/theme/shiki-theme.js +328 -0
  159. package/dist/theme/shiki-theme.js.map +1 -0
  160. package/dist/theme/tasty-config.d.ts +13 -0
  161. package/dist/theme/tasty-config.d.ts.map +1 -0
  162. package/dist/theme/tasty-config.js +16 -0
  163. package/dist/theme/tasty-config.js.map +1 -0
  164. package/dist/ui.d.ts +6 -0
  165. package/dist/ui.d.ts.map +1 -0
  166. package/dist/ui.js +53 -0
  167. package/dist/ui.js.map +1 -0
  168. package/dist/versioning.d.ts +11 -0
  169. package/dist/versioning.d.ts.map +1 -0
  170. package/dist/versioning.js +38 -0
  171. package/dist/versioning.js.map +1 -0
  172. package/package.json +92 -0
  173. package/tasty.config.mjs +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localization.js","names":[],"sources":["../src/localization.ts"],"sourcesContent":["import type { DocsConfig, DocsRoute, NavigationItem } from \"@tenphi/docs\";\nimport {\n normalizeNavigationPath,\n type ResolvedNavigationLayout,\n} from \"./navigation.js\";\n\ntype LocaleOptions = {\n locales?: DocsConfig[\"locales\"] | undefined;\n defaultLocale?: DocsConfig[\"defaultLocale\"] | undefined;\n translations?: DocsConfig[\"translations\"] | undefined;\n};\n\nexport function routeLocale(path: string, options: LocaleOptions): string {\n const first = normalizeNavigationPath(path).split(\"/\")[1];\n return first && first !== \"root\" && options.locales?.[first] ? first : \"root\";\n}\n\n/** Author components can render before the page initializes route locals. */\nexport function requestLanguage(\n pathname: string,\n base: string,\n options: LocaleOptions,\n): string {\n const prefix = base.replace(/\\/$/, \"\");\n const path =\n prefix && (pathname === prefix || pathname.startsWith(`${prefix}/`))\n ? pathname.slice(prefix.length)\n : pathname;\n const locale = routeLocale(path, options);\n return options.locales?.[locale]?.lang ?? (locale === \"root\" ? \"en\" : locale);\n}\n\nexport function localePath(path: string, options: LocaleOptions): string {\n const route = normalizeNavigationPath(path);\n const locale = routeLocale(route, options);\n return locale === \"root\"\n ? route\n : normalizeNavigationPath(route.slice(locale.length + 1));\n}\n\nexport function localizedRoute(\n path: string,\n locale: string,\n options: LocaleOptions,\n): string {\n const suffix = localePath(path, options);\n return normalizeNavigationPath(\n `${locale === \"root\" ? \"\" : `/${locale}`}${suffix}`,\n );\n}\n\n/** Missing translations use a real default-language page, then a real locale home. */\nexport function localeTarget(\n path: string,\n locale: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n): string | undefined {\n const visible = new Set(\n routes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n const fallback =\n options.defaultLocale ??\n (options.locales?.root\n ? \"root\"\n : (Object.keys(options.locales ?? {})[0] ?? \"root\"));\n return [\n localizedRoute(path, locale, options),\n localizedRoute(path, fallback, options),\n localizedRoute(\"/\", locale, options),\n localizedRoute(\"/\", fallback, options),\n ].find((route) => visible.has(route));\n}\n\nexport function localeHref(\n link: string,\n currentPath: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n base = \"/\",\n): string {\n if (!link.startsWith(\"/\") || link.startsWith(\"//\")) return link;\n const suffix = /[?#].*$/.exec(link)?.[0] ?? \"\";\n const path = suffix ? link.slice(0, -suffix.length) : link;\n const target = options.locales\n ? localeTarget(path, routeLocale(currentPath, options), routes, options)\n : normalizeNavigationPath(path);\n return (\n `${base.replace(/\\/$/, \"\")}${target ?? normalizeNavigationPath(path)}${suffix}` ||\n \"/\"\n );\n}\n\n/** Resolve navigation against the current locale's available routes. */\nexport function localizedNavigation(\n layout: ResolvedNavigationLayout,\n routes: DocsRoute[],\n currentPath: string,\n options: LocaleOptions,\n): { layout: ResolvedNavigationLayout; routes: DocsRoute[] } {\n if (!options.locales) return { layout, routes };\n const locale = routeLocale(currentPath, options);\n const language =\n options.locales[locale]?.lang ?? (locale === \"root\" ? \"en\" : locale);\n const label = (value: string) =>\n options.translations?.[language]?.[`navigation.${value}`] ??\n options.translations?.[language.split(\"-\")[0]!]?.[`navigation.${value}`] ??\n value;\n const localRoutes = routes\n .filter((route) => routeLocale(route.route, options) === locale)\n .map((route) => ({ ...route, route: localePath(route.route, options) }));\n const available = new Set(\n localRoutes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n const convert = (items: NavigationItem[]): NavigationItem[] =>\n items.flatMap((item): NavigationItem[] => {\n const link = typeof item === \"string\" ? item : item.link;\n const internal = link?.startsWith(\"/\") && !link.startsWith(\"//\");\n const translated = internal ? localePath(link!, options) : link;\n if (typeof item === \"string\")\n return !internal || available.has(translated!) ? [translated!] : [];\n if (\"items\" in item || \"autogenerate\" in item) {\n const { link: _link, ...group } = item;\n return [\n {\n ...group,\n label: label(group.label),\n ...(translated && (!internal || available.has(translated))\n ? { link: translated }\n : {}),\n ...(\"items\" in item\n ? { items: convert(item.items) }\n : {\n autogenerate: {\n directory: localePath(item.autogenerate.directory, options),\n },\n }),\n },\n ];\n }\n return !internal || available.has(translated!)\n ? [{ ...item, label: label(item.label), link: translated! }]\n : [];\n });\n return {\n layout: {\n ...layout,\n ...(layout.items ? { items: convert(layout.items) } : {}),\n tabs: layout.tabs.map((tab) => ({\n ...tab,\n label: label(tab.label),\n link:\n tab.link.startsWith(\"/\") && !tab.link.startsWith(\"//\")\n ? localePath(tab.link, options)\n : tab.link,\n ...(tab.items ? { items: convert(tab.items) } : {}),\n })),\n },\n routes: localRoutes,\n };\n}\n\nexport const ENGLISH_MESSAGES = {\n copyPage: \"Copy page\",\n viewMarkdown: \"Download Markdown\",\n pageCopied: \"Page copied\",\n pageCopyError: \"Could not copy. Download Markdown to read or copy the text.\",\n draftTitle: \"Draft\",\n draftNotice:\n \"This page is available by direct link and excluded from navigation and indexes.\",\n searchError: \"Search could not load. Close and try again.\",\n appearance: \"Appearance\",\n colorScheme: \"Color scheme\",\n contrast: \"Contrast\",\n normalContrast: \"Normal\",\n highContrast: \"High\",\n auto: \"Auto\",\n closeNavigation: \"Close navigation\",\n primary: \"Primary\",\n sections: \"Sections\",\n chooseSection: \"Choose section\",\n headerLinks: \"Header links\",\n more: \"More\",\n closeMore: \"Close more menu\",\n moreLinks: \"More links\",\n versions: \"Versions\",\n documentationVersion: \"Documentation version\",\n documentationVersions: \"Documentation versions\",\n version: \"Version\",\n home: \"Home\",\n generatedWith: \"Generated with\",\n viewSource: \"View source\",\n copyCode: \"Copy code\",\n codeCopied: \"Code copied\",\n codeCopyError: \"Could not copy code\",\n copyHeading: \"Copy link to “{heading}”\",\n linkCopied: \"Link copied\",\n linkCopyError: \"Could not copy link\",\n notFound: \"Page not found\",\n notFoundDescription: \"The page you requested could not be found.\",\n fallback: \"fallback\",\n} as const;\nexport type MessageKey = keyof typeof ENGLISH_MESSAGES;\nconst FRENCH_MESSAGES: Record<MessageKey, string> = {\n copyPage: \"Copier la page\",\n viewMarkdown: \"Télécharger le Markdown\",\n pageCopied: \"Page copiée\",\n pageCopyError:\n \"Copie impossible. Téléchargez le Markdown pour lire ou copier le texte.\",\n draftTitle: \"Brouillon\",\n draftNotice:\n \"Cette page est accessible par son lien direct et exclue de la navigation et des index.\",\n searchError: \"La recherche n’a pas pu se charger. Fermez et réessayez.\",\n appearance: \"Apparence\",\n colorScheme: \"Thème de couleur\",\n contrast: \"Contraste\",\n normalContrast: \"Normal\",\n highContrast: \"Élevé\",\n auto: \"Auto\",\n closeNavigation: \"Fermer la navigation\",\n primary: \"Navigation principale\",\n sections: \"Sections\",\n chooseSection: \"Choisir une section\",\n headerLinks: \"Liens d’en-tête\",\n more: \"Plus\",\n closeMore: \"Fermer le menu\",\n moreLinks: \"Autres liens\",\n versions: \"Versions\",\n documentationVersion: \"Version de la documentation\",\n documentationVersions: \"Versions de la documentation\",\n version: \"Version\",\n home: \"Accueil\",\n generatedWith: \"Créé avec\",\n viewSource: \"Voir le code source\",\n copyCode: \"Copier le code\",\n codeCopied: \"Code copié\",\n codeCopyError: \"Impossible de copier le code\",\n copyHeading: \"Copier le lien vers « {heading} »\",\n linkCopied: \"Lien copié\",\n linkCopyError: \"Impossible de copier le lien\",\n notFound: \"Page introuvable\",\n notFoundDescription: \"La page demandée est introuvable.\",\n fallback: \"langue de secours\",\n};\nexport function messages(\n lang: string,\n options: LocaleOptions = {},\n): Record<MessageKey, string> {\n const language = lang.split(\"-\")[0] ?? \"en\";\n return {\n ...ENGLISH_MESSAGES,\n ...(language === \"fr\" ? FRENCH_MESSAGES : {}),\n ...options.translations?.[language],\n ...options.translations?.[lang],\n };\n}\n\nexport function localeAlternates(\n path: string,\n routes: DocsRoute[],\n options: LocaleOptions,\n): Array<{ lang: string; route: string }> {\n const available = new Set(\n routes\n .filter((route) => route.discoverable !== false)\n .map((route) => route.route),\n );\n if (!options.locales || !available.has(path)) return [];\n const alternates = Object.entries(options.locales).flatMap(\n ([locale, config]) => {\n const route = localizedRoute(path, locale, options);\n return available.has(route)\n ? [{ lang: config.lang ?? locale, route }]\n : [];\n },\n );\n const fallback = localizedRoute(\n path,\n options.defaultLocale ??\n (options.locales.root ? \"root\" : Object.keys(options.locales)[0]!),\n options,\n );\n if (available.has(fallback))\n alternates.push({ lang: \"x-default\", route: fallback });\n return alternates;\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ //#region src/markdown/rendered-content.d.ts
2
+ /** Wrap rendered headings with a visible, accessible permalink. */
3
+ declare function addHeadingPermalinks(html: string): string;
4
+ //#endregion
5
+ export { addHeadingPermalinks };
6
+ //# sourceMappingURL=rendered-content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendered-content.d.ts","names":[],"sources":["../../src/markdown/rendered-content.ts"],"mappings":";;iBAMgB,qBAAqB"}
@@ -0,0 +1,34 @@
1
+ //#region src/markdown/rendered-content.ts
2
+ const headingPattern = /<div\b[^>]*\bclass=["'][^"']*\bcookbook-heading-wrapper\b[^"']*["'][^>]*>[\s\S]*?<\/div>|<h([1-6])\b([^>]*)>([\s\S]*?)<\/h\1>/gi;
3
+ const idPattern = /\sid=(['"])(.*?)\1/i;
4
+ const tags = /<[^>]*>/g;
5
+ /** Wrap rendered headings with a visible, accessible permalink. */
6
+ function addHeadingPermalinks(html) {
7
+ return html.replace(headingPattern, (heading, level, attributes, content) => {
8
+ if (!level || attributes === void 0 || content === void 0) return heading;
9
+ const id = idPattern.exec(attributes)?.[2];
10
+ if (!id) return heading;
11
+ const title = escapeAttribute(decodeEntities(content.replace(tags, "").trim()));
12
+ return `<div class="cookbook-heading-wrapper level-h${level}">${heading}<a class="cookbook-anchor-link" href="#${escapeAttribute(id)}" aria-label="Permalink to “${title}”"><span class="cookbook-anchor-icon" aria-hidden="true">#</span></a></div>`;
13
+ });
14
+ }
15
+ function decodeEntities(value) {
16
+ const named = {
17
+ amp: "&",
18
+ apos: "'",
19
+ gt: ">",
20
+ lt: "<",
21
+ quot: "\""
22
+ };
23
+ return value.replace(/&(?:#(\d+)|#x([\da-f]+)|(amp|apos|gt|lt|quot));/gi, (entity, decimal, hexadecimal, name) => {
24
+ const point = decimal ? Number.parseInt(decimal, 10) : hexadecimal ? Number.parseInt(hexadecimal, 16) : void 0;
25
+ return point === void 0 ? named[name.toLowerCase()] ?? entity : String.fromCodePoint(point);
26
+ });
27
+ }
28
+ function escapeAttribute(value) {
29
+ return value.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
30
+ }
31
+ //#endregion
32
+ export { addHeadingPermalinks };
33
+
34
+ //# sourceMappingURL=rendered-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rendered-content.js","names":[],"sources":["../../src/markdown/rendered-content.ts"],"sourcesContent":["const headingPattern =\n /<div\\b[^>]*\\bclass=[\"'][^\"']*\\bcookbook-heading-wrapper\\b[^\"']*[\"'][^>]*>[\\s\\S]*?<\\/div>|<h([1-6])\\b([^>]*)>([\\s\\S]*?)<\\/h\\1>/gi;\nconst idPattern = /\\sid=(['\"])(.*?)\\1/i;\nconst tags = /<[^>]*>/g;\n\n/** Wrap rendered headings with a visible, accessible permalink. */\nexport function addHeadingPermalinks(html: string): string {\n return html.replace(\n headingPattern,\n (\n heading,\n level: string | undefined,\n attributes: string | undefined,\n content: string | undefined,\n ) => {\n // Consume an existing heading wrapper unchanged so its inner heading\n // is not matched and wrapped a second time.\n if (!level || attributes === undefined || content === undefined) {\n return heading;\n }\n const id = idPattern.exec(attributes)?.[2];\n if (!id) return heading;\n const title = escapeAttribute(\n decodeEntities(content.replace(tags, \"\").trim()),\n );\n return `<div class=\"cookbook-heading-wrapper level-h${level}\">${heading}<a class=\"cookbook-anchor-link\" href=\"#${escapeAttribute(id)}\" aria-label=\"Permalink to “${title}”\"><span class=\"cookbook-anchor-icon\" aria-hidden=\"true\">#</span></a></div>`;\n },\n );\n}\n\nfunction decodeEntities(value: string): string {\n const named: Record<string, string> = {\n amp: \"&\",\n apos: \"'\",\n gt: \">\",\n lt: \"<\",\n quot: '\"',\n };\n return value.replace(\n /&(?:#(\\d+)|#x([\\da-f]+)|(amp|apos|gt|lt|quot));/gi,\n (entity, decimal: string, hexadecimal: string, name: string) => {\n const point = decimal\n ? Number.parseInt(decimal, 10)\n : hexadecimal\n ? Number.parseInt(hexadecimal, 16)\n : undefined;\n return point === undefined\n ? (named[name.toLowerCase()] ?? entity)\n : String.fromCodePoint(point);\n },\n );\n}\n\nfunction escapeAttribute(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#39;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\");\n}\n"],"mappings":";AAAA,MAAM,iBACJ;AACF,MAAM,YAAY;AAClB,MAAM,OAAO;;AAGb,SAAgB,qBAAqB,MAAsB;CACzD,OAAO,KAAK,QACV,iBAEE,SACA,OACA,YACA,YACG;EAGH,IAAI,CAAC,SAAS,eAAe,KAAA,KAAa,YAAY,KAAA,GACpD,OAAO;EAET,MAAM,KAAK,UAAU,KAAK,UAAU,CAAC,GAAG;EACxC,IAAI,CAAC,IAAI,OAAO;EAChB,MAAM,QAAQ,gBACZ,eAAe,QAAQ,QAAQ,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CACjD;EACA,OAAO,+CAA+C,MAAM,IAAI,QAAQ,yCAAyC,gBAAgB,EAAE,EAAE,8BAA8B,MAAM;CAC3K,CACF;AACF;AAEA,SAAS,eAAe,OAAuB;CAC7C,MAAM,QAAgC;EACpC,KAAK;EACL,MAAM;EACN,IAAI;EACJ,IAAI;EACJ,MAAM;CACR;CACA,OAAO,MAAM,QACX,sDACC,QAAQ,SAAiB,aAAqB,SAAiB;EAC9D,MAAM,QAAQ,UACV,OAAO,SAAS,SAAS,EAAE,IAC3B,cACE,OAAO,SAAS,aAAa,EAAE,IAC/B,KAAA;EACN,OAAO,UAAU,KAAA,IACZ,MAAM,KAAK,YAAY,MAAM,SAC9B,OAAO,cAAc,KAAK;CAChC,CACF;AACF;AAEA,SAAS,gBAAgB,OAAuB;CAC9C,OAAO,MACJ,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,MAAK,QAAQ,CAAC,CACzB,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM;AAC3B"}
@@ -0,0 +1,14 @@
1
+ import { ENGLISH_MESSAGES } from "./localization.js";
2
+ //#region src/client/messages.ts
3
+ function message(key) {
4
+ try {
5
+ const custom = JSON.parse(document.querySelector("meta[name=\"cookbook-messages\"]")?.getAttribute("content") ?? "{}");
6
+ return typeof custom[key] === "string" ? custom[key] : ENGLISH_MESSAGES[key];
7
+ } catch {
8
+ return ENGLISH_MESSAGES[key];
9
+ }
10
+ }
11
+ //#endregion
12
+ export { message as t };
13
+
14
+ //# sourceMappingURL=messages-B_3xUzUb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages-B_3xUzUb.js","names":[],"sources":["../src/client/messages.ts"],"sourcesContent":["import { ENGLISH_MESSAGES, type MessageKey } from \"../localization.js\";\nexport function message(key: MessageKey): string {\n try {\n const custom = JSON.parse(\n document\n .querySelector('meta[name=\"cookbook-messages\"]')\n ?.getAttribute(\"content\") ?? \"{}\",\n );\n return typeof custom[key] === \"string\"\n ? custom[key]\n : ENGLISH_MESSAGES[key];\n } catch {\n return ENGLISH_MESSAGES[key];\n }\n}\n"],"mappings":";;AACA,SAAgB,QAAQ,KAAyB;CAC/C,IAAI;EACF,MAAM,SAAS,KAAK,MAClB,SACG,cAAc,kCAAgC,CAAC,EAC9C,aAAa,SAAS,KAAK,IACjC;EACA,OAAO,OAAO,OAAO,SAAS,WAC1B,OAAO,OACP,iBAAiB;CACvB,QAAQ;EACN,OAAO,iBAAiB;CAC1B;AACF"}
@@ -0,0 +1,31 @@
1
+ import { DocsConfig, DocsRoute, NavigationItem, NavigationTab } from "@tenphi/docs";
2
+ //#region src/navigation.d.ts
3
+ interface ResolvedNavigationTab extends NavigationTab {
4
+ /** Index of the tab's resolved sidebar group. */
5
+ sidebarGroup?: number;
6
+ }
7
+ interface ResolvedNavigationLayout {
8
+ fallbackSidebarGroup?: number;
9
+ items?: NavigationItem[];
10
+ tabs: ResolvedNavigationTab[];
11
+ sectioned: boolean;
12
+ }
13
+ type PageSidebarItem = {
14
+ label: string;
15
+ link: string;
16
+ attrs?: Record<string, string>;
17
+ } | {
18
+ label: string;
19
+ items: PageSidebarItem[];
20
+ };
21
+ declare function resolveNavigationLayout(navigation: DocsConfig["navigation"]): ResolvedNavigationLayout;
22
+ declare function navigationItemsForPath(layout: ResolvedNavigationLayout, pathname: string): NavigationItem[] | undefined;
23
+ /** Build a sidebar for graph-backed custom pages. */
24
+ declare function pageSidebar(layout: ResolvedNavigationLayout, routes: DocsRoute[]): PageSidebarItem[];
25
+ /** Pick one tab, preferring its own URL before routes owned by its sidebar. */
26
+ declare function activeNavigationTab(tabs: NavigationTab[], pathname: string): number;
27
+ declare function navigationPath(pathname: string, base?: string): string;
28
+ declare function normalizeNavigationPath(path: string): string;
29
+ //#endregion
30
+ export { PageSidebarItem, ResolvedNavigationLayout, ResolvedNavigationTab, activeNavigationTab, navigationItemsForPath, navigationPath, normalizeNavigationPath, pageSidebar, resolveNavigationLayout };
31
+ //# sourceMappingURL=navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.d.ts","names":[],"sources":["../src/navigation.ts"],"mappings":";;UAOiB,8BAA8B;;EAE7C;;UAGe;EACf;EACA,QAAQ;EACR,MAAM;EACN;;KAGU;EACN;EAAe;EAAc,QAAQ;;EACrC;EAAe,OAAO;;iBAEZ,wBACd,YAAY,2BACX;iBAoBa,uBACd,QAAQ,0BACR,mBACC;;iBAQa,YACd,QAAQ,0BACR,QAAQ,cACP;;iBA0Ia,oBACd,MAAM,iBACN;iBAoBc,eAAe,kBAAkB;iBAUjC,wBAAwB"}
@@ -0,0 +1,165 @@
1
+ //#region src/navigation.ts
2
+ function resolveNavigationLayout(navigation) {
3
+ const items = Array.isArray(navigation) ? navigation : navigation?.items;
4
+ const configuredTabs = Array.isArray(navigation) ? [] : navigation?.tabs ?? [];
5
+ const fallbackSidebarGroup = items !== void 0 ? 0 : void 0;
6
+ let sidebarGroup = fallbackSidebarGroup === void 0 ? 0 : 1;
7
+ const tabs = configuredTabs.map((tab) => ({
8
+ ...tab,
9
+ ...tab.items !== void 0 ? { sidebarGroup: sidebarGroup++ } : {}
10
+ }));
11
+ return {
12
+ ...fallbackSidebarGroup !== void 0 ? { fallbackSidebarGroup } : {},
13
+ ...items !== void 0 ? { items } : {},
14
+ tabs,
15
+ sectioned: tabs.some((tab) => tab.sidebarGroup !== void 0)
16
+ };
17
+ }
18
+ function navigationItemsForPath(layout, pathname) {
19
+ const active = activeNavigationTab(layout.tabs, pathname);
20
+ return active >= 0 && layout.tabs[active]?.items !== void 0 ? layout.tabs[active].items : layout.items;
21
+ }
22
+ /** Build a sidebar for graph-backed custom pages. */
23
+ function pageSidebar(layout, routes) {
24
+ const visibleRoutes = routes.filter((route) => route.sidebar !== false && route.discoverable !== false).toSorted((left, right) => {
25
+ const leftOrder = typeof left.sidebar === "object" ? left.sidebar.order : void 0;
26
+ const rightOrder = typeof right.sidebar === "object" ? right.sidebar.order : void 0;
27
+ return (leftOrder ?? Number.POSITIVE_INFINITY) - (rightOrder ?? Number.POSITIVE_INFINITY) || left.route.localeCompare(right.route);
28
+ });
29
+ const titles = new Map(visibleRoutes.map((route) => [route.route, typeof route.sidebar === "object" && route.sidebar.label ? route.sidebar.label : route.title]));
30
+ const labelFor = (route) => titles.get(normalizeNavigationPath(route)) ?? normalizeNavigationPath(route).split("/").filter(Boolean).at(-1) ?? "Home";
31
+ const generatedItems = (directory) => {
32
+ const root = normalizeNavigationPath(directory);
33
+ return groupedRoutes(visibleRoutes.filter((route) => root === "/" || route.route === root || route.route.startsWith(`${root}/`)));
34
+ };
35
+ const convert = (item) => {
36
+ if (typeof item === "string") return {
37
+ label: labelFor(item),
38
+ link: item
39
+ };
40
+ if ("items" in item || "autogenerate" in item) {
41
+ let children = "items" in item ? item.items.map(convert) : generatedItems(item.autogenerate.directory);
42
+ if (item.link) {
43
+ children = withoutRoute(children, item.link);
44
+ if (!children.length) return {
45
+ label: item.label,
46
+ link: item.link
47
+ };
48
+ children.unshift({
49
+ label: item.label,
50
+ link: item.link,
51
+ attrs: { "data-cookbook-group-link": "" }
52
+ });
53
+ }
54
+ return {
55
+ label: item.label,
56
+ items: children
57
+ };
58
+ }
59
+ return item;
60
+ };
61
+ const fallback = layout.items?.length ? layout.items.map(convert) : groupedRoutes(visibleRoutes);
62
+ const sidebar = !layout.sectioned ? fallback : [...layout.fallbackSidebarGroup !== void 0 ? [{
63
+ label: "Documentation",
64
+ items: fallback
65
+ }] : [], ...layout.tabs.flatMap((tab) => tab.items !== void 0 ? [{
66
+ label: tab.label,
67
+ items: tab.items.map(convert)
68
+ }] : [])];
69
+ return routes.filter((route) => route.discoverable === false).reduce((items, route) => withoutRoute(items, route.route), sidebar);
70
+ }
71
+ function withoutRoute(items, route) {
72
+ const target = normalizeNavigationPath(route);
73
+ return items.flatMap((item) => {
74
+ if ("link" in item) return !/^(?:[a-z][a-z\d+.-]*:|\/\/|[?#])/i.test(item.link) && normalizeNavigationPath(item.link) === target ? [] : [item];
75
+ const children = withoutRoute(item.items, route);
76
+ const first = children[0];
77
+ if (children.length === 1 && first && "link" in first && first.attrs?.["data-cookbook-group-link"] !== void 0) return [{
78
+ label: item.label,
79
+ link: first.link
80
+ }];
81
+ return children.length ? [{
82
+ ...item,
83
+ items: children
84
+ }] : [];
85
+ });
86
+ }
87
+ function groupedRoutes(routes) {
88
+ const items = [];
89
+ const groups = /* @__PURE__ */ new Map();
90
+ for (const route of routes) {
91
+ const item = {
92
+ label: typeof route.sidebar === "object" && route.sidebar.label ? route.sidebar.label : route.title,
93
+ link: route.route
94
+ };
95
+ const group = typeof route.sidebar === "object" ? route.sidebar.group : void 0;
96
+ if (!group) {
97
+ items.push(item);
98
+ continue;
99
+ }
100
+ const grouped = groups.get(group) ?? [];
101
+ grouped.push(item);
102
+ groups.set(group, grouped);
103
+ }
104
+ return [...items, ...Array.from(groups, ([label, grouped]) => ({
105
+ label,
106
+ items: grouped
107
+ }))];
108
+ }
109
+ /** Pick one tab, preferring its own URL before routes owned by its sidebar. */
110
+ function activeNavigationTab(tabs, pathname) {
111
+ const current = normalizeNavigationPath(pathname);
112
+ let winner = -1;
113
+ let winningScore = -1;
114
+ for (const [index, tab] of tabs.entries()) {
115
+ const score = Math.max(linkMatchScore(tab.link, current, 3e4), navigationMatchScore(tab.items ?? [], current));
116
+ if (score > winningScore) {
117
+ winner = index;
118
+ winningScore = score;
119
+ }
120
+ }
121
+ return winningScore >= 0 ? winner : -1;
122
+ }
123
+ function navigationPath(pathname, base = "/") {
124
+ const normalizedBase = normalizeNavigationPath(base);
125
+ return normalizeNavigationPath(normalizedBase !== "/" && (pathname === normalizedBase || pathname.startsWith(`${normalizedBase}/`)) ? pathname.slice(normalizedBase.length) || "/" : pathname);
126
+ }
127
+ function normalizeNavigationPath(path) {
128
+ const normalized = `/${(path.split(/[?#]/, 1)[0]?.replace(/\\/g, "/") ?? "/").replace(/^\/+|\/+$/g, "")}`;
129
+ return normalized === "/" ? normalized : normalized.replace(/\/$/, "");
130
+ }
131
+ function navigationMatchScore(items, current) {
132
+ let score = -1;
133
+ for (const item of items) {
134
+ if (typeof item === "string") {
135
+ if (normalizeNavigationPath(item) === current) score = Math.max(score, 2e4);
136
+ continue;
137
+ }
138
+ if (item.link) score = Math.max(score, linkMatchScore(item.link, current, 2e4));
139
+ if ("items" in item) {
140
+ score = Math.max(score, navigationMatchScore(item.items, current));
141
+ continue;
142
+ }
143
+ if ("autogenerate" in item) {
144
+ score = Math.max(score, directoryMatchScore(item.autogenerate.directory, current));
145
+ continue;
146
+ }
147
+ }
148
+ return score;
149
+ }
150
+ function linkMatchScore(link, current, base) {
151
+ if (!link.startsWith("/")) return -1;
152
+ const target = normalizeNavigationPath(link);
153
+ if (target === current) return base + 1e3 + target.length;
154
+ if (target !== "/" && current.startsWith(`${target}/`)) return base + target.length;
155
+ return -1;
156
+ }
157
+ function directoryMatchScore(directory, current) {
158
+ const target = normalizeNavigationPath(directory);
159
+ if (target === "/") return 1e4;
160
+ return current === target || current.startsWith(`${target}/`) ? 1e4 + target.length : -1;
161
+ }
162
+ //#endregion
163
+ export { activeNavigationTab, navigationItemsForPath, navigationPath, normalizeNavigationPath, pageSidebar, resolveNavigationLayout };
164
+
165
+ //# sourceMappingURL=navigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.js","names":[],"sources":["../src/navigation.ts"],"sourcesContent":["import type {\n DocsConfig,\n DocsRoute,\n NavigationItem,\n NavigationTab,\n} from \"@tenphi/docs\";\n\nexport interface ResolvedNavigationTab extends NavigationTab {\n /** Index of the tab's resolved sidebar group. */\n sidebarGroup?: number;\n}\n\nexport interface ResolvedNavigationLayout {\n fallbackSidebarGroup?: number;\n items?: NavigationItem[];\n tabs: ResolvedNavigationTab[];\n sectioned: boolean;\n}\n\nexport type PageSidebarItem =\n | { label: string; link: string; attrs?: Record<string, string> }\n | { label: string; items: PageSidebarItem[] };\n\nexport function resolveNavigationLayout(\n navigation: DocsConfig[\"navigation\"],\n): ResolvedNavigationLayout {\n const items = Array.isArray(navigation) ? navigation : navigation?.items;\n const configuredTabs = Array.isArray(navigation)\n ? []\n : (navigation?.tabs ?? []);\n const fallbackSidebarGroup = items !== undefined ? 0 : undefined;\n let sidebarGroup = fallbackSidebarGroup === undefined ? 0 : 1;\n const tabs = configuredTabs.map((tab) => ({\n ...tab,\n ...(tab.items !== undefined ? { sidebarGroup: sidebarGroup++ } : {}),\n }));\n\n return {\n ...(fallbackSidebarGroup !== undefined ? { fallbackSidebarGroup } : {}),\n ...(items !== undefined ? { items } : {}),\n tabs,\n sectioned: tabs.some((tab) => tab.sidebarGroup !== undefined),\n };\n}\n\nexport function navigationItemsForPath(\n layout: ResolvedNavigationLayout,\n pathname: string,\n): NavigationItem[] | undefined {\n const active = activeNavigationTab(layout.tabs, pathname);\n return active >= 0 && layout.tabs[active]?.items !== undefined\n ? layout.tabs[active].items\n : layout.items;\n}\n\n/** Build a sidebar for graph-backed custom pages. */\nexport function pageSidebar(\n layout: ResolvedNavigationLayout,\n routes: DocsRoute[],\n): PageSidebarItem[] {\n const visibleRoutes = routes\n .filter((route) => route.sidebar !== false && route.discoverable !== false)\n .toSorted((left, right) => {\n const leftOrder =\n typeof left.sidebar === \"object\" ? left.sidebar.order : undefined;\n const rightOrder =\n typeof right.sidebar === \"object\" ? right.sidebar.order : undefined;\n return (\n (leftOrder ?? Number.POSITIVE_INFINITY) -\n (rightOrder ?? Number.POSITIVE_INFINITY) ||\n left.route.localeCompare(right.route)\n );\n });\n const titles = new Map(\n visibleRoutes.map((route) => [\n route.route,\n typeof route.sidebar === \"object\" && route.sidebar.label\n ? route.sidebar.label\n : route.title,\n ]),\n );\n const labelFor = (route: string) =>\n titles.get(normalizeNavigationPath(route)) ??\n normalizeNavigationPath(route).split(\"/\").filter(Boolean).at(-1) ??\n \"Home\";\n const generatedItems = (directory: string) => {\n const root = normalizeNavigationPath(directory);\n return groupedRoutes(\n visibleRoutes.filter(\n (route) =>\n root === \"/\" ||\n route.route === root ||\n route.route.startsWith(`${root}/`),\n ),\n );\n };\n const convert = (item: NavigationItem): PageSidebarItem => {\n if (typeof item === \"string\") {\n return { label: labelFor(item), link: item };\n }\n if (\"items\" in item || \"autogenerate\" in item) {\n let children =\n \"items\" in item\n ? item.items.map(convert)\n : generatedItems(item.autogenerate.directory);\n if (item.link) {\n children = withoutRoute(children, item.link);\n // Keep the parent in route resolution and pagination. The sidebar\n // promotes this marked entry into the group header.\n if (!children.length) return { label: item.label, link: item.link };\n children.unshift({\n label: item.label,\n link: item.link,\n attrs: { \"data-cookbook-group-link\": \"\" },\n });\n }\n return {\n label: item.label,\n items: children,\n };\n }\n return item;\n };\n const fallback = layout.items?.length\n ? layout.items.map(convert)\n : groupedRoutes(visibleRoutes);\n const sidebar = !layout.sectioned\n ? fallback\n : [\n ...(layout.fallbackSidebarGroup !== undefined\n ? [{ label: \"Documentation\", items: fallback }]\n : []),\n ...layout.tabs.flatMap((tab) =>\n tab.items !== undefined\n ? [{ label: tab.label, items: tab.items.map(convert) }]\n : [],\n ),\n ];\n return routes\n .filter((route) => route.discoverable === false)\n .reduce((items, route) => withoutRoute(items, route.route), sidebar);\n}\n\nfunction withoutRoute(\n items: PageSidebarItem[],\n route: string,\n): PageSidebarItem[] {\n const target = normalizeNavigationPath(route);\n return items.flatMap((item): PageSidebarItem[] => {\n if (\"link\" in item) {\n return !/^(?:[a-z][a-z\\d+.-]*:|\\/\\/|[?#])/i.test(item.link) &&\n normalizeNavigationPath(item.link) === target\n ? []\n : [item];\n }\n const children = withoutRoute(item.items, route);\n const first = children[0];\n if (\n children.length === 1 &&\n first &&\n \"link\" in first &&\n first.attrs?.[\"data-cookbook-group-link\"] !== undefined\n ) {\n return [{ label: item.label, link: first.link }];\n }\n return children.length ? [{ ...item, items: children }] : [];\n });\n}\n\nfunction groupedRoutes(routes: DocsRoute[]): PageSidebarItem[] {\n const items: PageSidebarItem[] = [];\n const groups = new Map<string, PageSidebarItem[]>();\n for (const route of routes) {\n const item = {\n label:\n typeof route.sidebar === \"object\" && route.sidebar.label\n ? route.sidebar.label\n : route.title,\n link: route.route,\n };\n const group =\n typeof route.sidebar === \"object\" ? route.sidebar.group : undefined;\n if (!group) {\n items.push(item);\n continue;\n }\n const grouped = groups.get(group) ?? [];\n grouped.push(item);\n groups.set(group, grouped);\n }\n return [\n ...items,\n ...Array.from(groups, ([label, grouped]) => ({ label, items: grouped })),\n ];\n}\n\n/** Pick one tab, preferring its own URL before routes owned by its sidebar. */\nexport function activeNavigationTab(\n tabs: NavigationTab[],\n pathname: string,\n): number {\n const current = normalizeNavigationPath(pathname);\n let winner = -1;\n let winningScore = -1;\n\n for (const [index, tab] of tabs.entries()) {\n const score = Math.max(\n linkMatchScore(tab.link, current, 30_000),\n navigationMatchScore(tab.items ?? [], current),\n );\n if (score > winningScore) {\n winner = index;\n winningScore = score;\n }\n }\n\n return winningScore >= 0 ? winner : -1;\n}\n\nexport function navigationPath(pathname: string, base = \"/\"): string {\n const normalizedBase = normalizeNavigationPath(base);\n const withoutBase =\n normalizedBase !== \"/\" &&\n (pathname === normalizedBase || pathname.startsWith(`${normalizedBase}/`))\n ? pathname.slice(normalizedBase.length) || \"/\"\n : pathname;\n return normalizeNavigationPath(withoutBase);\n}\n\nexport function normalizeNavigationPath(path: string): string {\n const clean = path.split(/[?#]/, 1)[0]?.replace(/\\\\/g, \"/\") ?? \"/\";\n const normalized = `/${clean.replace(/^\\/+|\\/+$/g, \"\")}`;\n return normalized === \"/\" ? normalized : normalized.replace(/\\/$/, \"\");\n}\n\nfunction navigationMatchScore(\n items: NavigationItem[],\n current: string,\n): number {\n let score = -1;\n for (const item of items) {\n if (typeof item === \"string\") {\n if (normalizeNavigationPath(item) === current)\n score = Math.max(score, 20_000);\n continue;\n }\n if (item.link) {\n score = Math.max(score, linkMatchScore(item.link, current, 20_000));\n }\n if (\"items\" in item) {\n score = Math.max(score, navigationMatchScore(item.items, current));\n continue;\n }\n if (\"autogenerate\" in item) {\n score = Math.max(\n score,\n directoryMatchScore(item.autogenerate.directory, current),\n );\n continue;\n }\n }\n return score;\n}\n\nfunction linkMatchScore(link: string, current: string, base: number): number {\n if (!link.startsWith(\"/\")) return -1;\n const target = normalizeNavigationPath(link);\n if (target === current) return base + 1_000 + target.length;\n if (target !== \"/\" && current.startsWith(`${target}/`)) {\n return base + target.length;\n }\n return -1;\n}\n\nfunction directoryMatchScore(directory: string, current: string): number {\n const target = normalizeNavigationPath(directory);\n if (target === \"/\") return 10_000;\n return current === target || current.startsWith(`${target}/`)\n ? 10_000 + target.length\n : -1;\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ ---
2
+ import Callout from "../components/Callout.astro";
3
+ import { messages } from "../localization.js";
4
+ import { content } from "virtual:cookbook/config";
5
+ const ui = messages(Astro.locals.cookbookRoute.lang, content);
6
+ ---
7
+
8
+ <Callout title={ui.draftTitle}>{ui.draftNotice}</Callout>
@@ -0,0 +1,3 @@
1
+ ---
2
+ // This component intentionally renders nothing when the footer is disabled.
3
+ ---
@@ -0,0 +1,23 @@
1
+ ---
2
+ import PageActions from "../components/PageActions.astro";
3
+ import { messages } from "../localization.js";
4
+ import { content } from "virtual:cookbook/config";
5
+ import EditLink from "virtual:cookbook/components/EditLink";
6
+ import LastUpdated from "virtual:cookbook/components/LastUpdated";
7
+ import Pagination from "virtual:cookbook/components/Pagination";
8
+ import { FooterRoot } from "../components/LayoutComponents.js";
9
+ const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
10
+ ---
11
+
12
+ <FooterRoot className="td-footer" data-tasty-anatomy="Footer">
13
+ <div class="td-footer__meta">
14
+ <EditLink />
15
+ <LastUpdated />
16
+ </div>
17
+ <PageActions />
18
+ <Pagination />
19
+ <p class="td-footer__credit">
20
+ {ui.generatedWith}{" "}
21
+ <a href="https://cookbook.tenphi.me/">Cookbook</a>.
22
+ </p>
23
+ </FooterRoot>
@@ -0,0 +1,59 @@
1
+ ---
2
+ import { pageMetadata } from "../page-metadata.js";
3
+ import { content } from "virtual:cookbook/config";
4
+ import { localeAlternates, messages } from "../localization.js";
5
+ import { navigationPath } from "../navigation.js";
6
+ const current = navigationPath(Astro.url.pathname, content.base);
7
+ const entry = content.entries.find((entry) => entry.route === current);
8
+ const head = (
9
+ entry
10
+ ? pageMetadata(entry, content, Astro.locals.cookbookRoute.head)
11
+ : [
12
+ ...Astro.locals.cookbookRoute.head,
13
+ { tag: "meta", attrs: { name: "robots", content: "noindex, follow" } },
14
+ ]
15
+ ).filter(
16
+ (tag) =>
17
+ (tag.tag !== "link" ||
18
+ tag.attrs?.rel !== "sitemap" ||
19
+ content.routes.some((route) => route.sitemap !== false)) &&
20
+ (!content.locales ||
21
+ tag.tag !== "link" ||
22
+ tag.attrs?.rel !== "alternate" ||
23
+ !tag.attrs?.hreflang),
24
+ );
25
+ const indexedRoutes = content.routes.filter((route) => route.sitemap !== false);
26
+ if (Astro.site && indexedRoutes.some((route) => route.route === current)) {
27
+ for (const { lang, route } of localeAlternates(
28
+ current,
29
+ indexedRoutes,
30
+ content,
31
+ ))
32
+ head.push({
33
+ tag: "link",
34
+ attrs: {
35
+ rel: "alternate",
36
+ hreflang: lang,
37
+ href: new URL(
38
+ `${content.base.replace(/\/$/, "")}${route === "/" ? "/" : `${route}/`}`,
39
+ Astro.site,
40
+ ).href,
41
+ },
42
+ });
43
+ }
44
+ const ui = messages(Astro.locals.cookbookRoute.lang, content);
45
+ ---
46
+
47
+ {
48
+ !entry && (
49
+ <title>
50
+ {`${Astro.locals.cookbookRoute.entry.data.title} | ${content.site.title}`}
51
+ </title>
52
+ )
53
+ }
54
+ {
55
+ head.map(({ tag: Tag, attrs, content }) => (
56
+ <Tag {...attrs} set:html={content} />
57
+ ))
58
+ }
59
+ <meta name="cookbook-messages" content={JSON.stringify(ui)} />
@@ -0,0 +1,139 @@
1
+ ---
2
+ import {
3
+ localizedNavigation,
4
+ localePath,
5
+ localeHref,
6
+ } from "../localization.js";
7
+ import { messages } from "../localization.js";
8
+ import LanguageSelect from "virtual:cookbook/components/LanguageSelect";
9
+ import Search from "virtual:cookbook/components/Search";
10
+ import SiteTitle from "virtual:cookbook/components/SiteTitle";
11
+ import SocialIcons from "virtual:cookbook/components/SocialIcons";
12
+ import MobileMenuToggle from "virtual:cookbook/components/MobileMenuToggle";
13
+ import HeaderLinks from "../components/HeaderLinks.astro";
14
+ import ThemeSelect from "virtual:cookbook/components/ThemeSelect";
15
+ import { content } from "virtual:cookbook/config";
16
+ import { layout as configuredLayout } from "virtual:cookbook/layout";
17
+ import { activeNavigationTab, navigationPath } from "../navigation.js";
18
+ import GlobalStyles from "../components/GlobalStyles.js";
19
+ import Logo from "../components/SiteLogo.astro";
20
+ import PackageVersion from "../components/PackageVersion.astro";
21
+ import VersionSwitcher from "../components/VersionSwitcher.astro";
22
+ import {
23
+ HeaderRoot,
24
+ TopNavigationRoot,
25
+ } from "../components/LayoutComponents.js";
26
+
27
+ const shouldRenderSearch = content.search;
28
+ const base = import.meta.env.BASE_URL.replace(/\/$/, "");
29
+ const currentPath = navigationPath(Astro.url.pathname, base || "/");
30
+ const { layout } = localizedNavigation(
31
+ configuredLayout,
32
+ content.routes,
33
+ currentPath,
34
+ content,
35
+ );
36
+ const activeTab = activeNavigationTab(
37
+ layout.tabs,
38
+ localePath(currentPath, content),
39
+ );
40
+ Astro.locals.cookbookRoute.siteTitleHref = localeHref(
41
+ content.logo ? (content.logo.href ?? "/") : "/",
42
+ currentPath,
43
+ content.routes,
44
+ content,
45
+ base || "/",
46
+ );
47
+ const { siteTitle } = Astro.locals.cookbookRoute;
48
+
49
+ function tabHref(link: string): string {
50
+ return localeHref(link, currentPath, content.routes, content, base || "/");
51
+ }
52
+ const ui = messages(Astro.locals.cookbookRoute?.lang ?? "en", content);
53
+ ---
54
+
55
+ <GlobalStyles />
56
+ <HeaderRoot
57
+ className={`td-header${layout.tabs.length ? " td-header--with-tabs" : ""}`}
58
+ data-tasty-anatomy="Header"
59
+ data-has-sidebar={Astro.locals.cookbookRoute.hasSidebar ? "" : undefined}
60
+ >
61
+ <div class="td-header__primary">
62
+ <div class="td-header__title">
63
+ {
64
+ content.logo !== false && (
65
+ <a
66
+ class="td-header__logo-link"
67
+ href={localeHref(
68
+ content.logo ? (content.logo.href ?? "/") : "/",
69
+ currentPath,
70
+ content.routes,
71
+ content,
72
+ base || "/",
73
+ )}
74
+ aria-label={
75
+ content.logo && !content.logo.decorative
76
+ ? undefined
77
+ : `${siteTitle}: ${ui.home}`
78
+ }
79
+ >
80
+ <Logo class="td-header__logo" />
81
+ </a>
82
+ )
83
+ }
84
+ <SiteTitle />
85
+ {
86
+ !content.site.versions && (
87
+ <PackageVersion version={content.site.version} />
88
+ )
89
+ }
90
+ </div>
91
+ {
92
+ content.site.versions && (
93
+ <VersionSwitcher
94
+ versions={content.site.versions}
95
+ routes={content.routes}
96
+ currentPath={currentPath}
97
+ base={base || "/"}
98
+ />
99
+ )
100
+ }
101
+ <div class="td-header__search cookbook-print-hidden">
102
+ {shouldRenderSearch && <Search />}
103
+ </div>
104
+ <HeaderLinks />
105
+ <div
106
+ class="td-header__mobile-theme md:cookbook-hidden cookbook-print-hidden"
107
+ >
108
+ <ThemeSelect />
109
+ </div>
110
+ <div
111
+ class="td-header__tools cookbook-hidden md:cookbook-flex cookbook-print-hidden"
112
+ >
113
+ <div class="td-header__social"><SocialIcons /></div>
114
+ <ThemeSelect />
115
+ <LanguageSelect />
116
+ </div>
117
+ </div>
118
+ {
119
+ layout.tabs.length > 0 && (
120
+ <TopNavigationRoot
121
+ className="td-top-tabs cookbook-print-hidden"
122
+ aria-label={ui.primary}
123
+ data-tasty-anatomy="TopNavigation"
124
+ >
125
+ {layout.tabs.map((tab, index) => (
126
+ <a
127
+ href={tabHref(tab.link)}
128
+ aria-current={index === activeTab ? "page" : undefined}
129
+ >
130
+ {tab.label}
131
+ </a>
132
+ ))}
133
+ </TopNavigationRoot>
134
+ )
135
+ }
136
+ {Astro.locals.cookbookRoute.hasSidebar && <MobileMenuToggle />}
137
+ </HeaderRoot>
138
+
139
+ <script src="../client/code-copy.js"></script>