@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,87 @@
1
+ //#region src/client/search.ts
2
+ /** Own the dialog lifecycle; Pagefind owns indexing, filtering and result rendering. */
3
+ function initializeSearch(element, options, development, base) {
4
+ if (element.dataset.initialized) return;
5
+ element.dataset.initialized = "true";
6
+ const open = element.querySelector("[data-open-modal]");
7
+ const close = element.querySelector("[data-close-modal]");
8
+ const dialog = element.querySelector("dialog");
9
+ const frame = element.querySelector(".dialog-frame");
10
+ const status = element.querySelector("[data-search-status]");
11
+ const shortcut = open.querySelector("kbd");
12
+ if (/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
13
+ shortcut.querySelector("kbd").textContent = "⌘";
14
+ open.setAttribute("aria-keyshortcuts", "Meta+K");
15
+ }
16
+ delete shortcut.dataset.pending;
17
+ let initialized;
18
+ let returnFocus = null;
19
+ const initialize = () => {
20
+ if (development) return Promise.resolve();
21
+ return initialized ??= (async () => {
22
+ const { PagefindUI } = await import("@pagefind/default-ui");
23
+ const translations = JSON.parse(element.dataset.translations ?? "{}");
24
+ const format = (url) => element.hasAttribute("data-strip-trailing-slash") ? url.replace(/(.)\/(#.*)?$/, "$1$2") : url;
25
+ new PagefindUI({
26
+ ...options,
27
+ element: "#cookbook__search",
28
+ baseUrl: base,
29
+ bundlePath: `${base.replace(/\/$/, "")}/pagefind/`,
30
+ showImages: false,
31
+ showSubResults: true,
32
+ translations,
33
+ processResult(result) {
34
+ result = options.processResult?.(result) ?? result;
35
+ result.url = format(result.url);
36
+ result.sub_results?.forEach((child) => {
37
+ child.url = format(child.url);
38
+ });
39
+ return result;
40
+ }
41
+ });
42
+ })().catch((error) => {
43
+ initialized = void 0;
44
+ if (status) {
45
+ status.hidden = false;
46
+ const translations = JSON.parse(element.dataset.translations ?? "{}");
47
+ status.textContent = translations.error ?? "Search could not load. Close and try again.";
48
+ }
49
+ throw error;
50
+ });
51
+ };
52
+ const show = async () => {
53
+ if (dialog.open) return;
54
+ returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : open;
55
+ dialog.showModal();
56
+ document.body.setAttribute("data-search-modal-open", "");
57
+ if (!development && status) status.hidden = true;
58
+ try {
59
+ await initialize();
60
+ if (dialog.open) element.querySelector("input")?.focus();
61
+ } catch {}
62
+ };
63
+ open.addEventListener("click", () => {
64
+ show();
65
+ });
66
+ open.disabled = false;
67
+ close.addEventListener("click", () => dialog.close());
68
+ dialog.addEventListener("click", (event) => {
69
+ if (!(event.target instanceof Element)) return;
70
+ if (event.target.closest("a[href]") || !frame.contains(event.target)) dialog.close();
71
+ });
72
+ dialog.addEventListener("close", () => {
73
+ document.body.removeAttribute("data-search-modal-open");
74
+ returnFocus?.focus();
75
+ });
76
+ window.addEventListener("keydown", (event) => {
77
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
78
+ event.preventDefault();
79
+ if (dialog.open) dialog.close();
80
+ else show();
81
+ }
82
+ });
83
+ }
84
+ //#endregion
85
+ export { initializeSearch };
86
+
87
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","names":[],"sources":["../../src/client/search.ts"],"sourcesContent":["type SearchResult = { url: string; sub_results?: SearchResult[] };\ntype PagefindOptions = Record<string, unknown> & {\n processResult?: (result: SearchResult) => SearchResult | void;\n};\n\n/** Own the dialog lifecycle; Pagefind owns indexing, filtering and result rendering. */\nexport function initializeSearch(\n element: HTMLElement,\n options: PagefindOptions,\n development: boolean,\n base: string,\n): void {\n if (element.dataset.initialized) return;\n element.dataset.initialized = \"true\";\n const open = element.querySelector<HTMLButtonElement>(\"[data-open-modal]\")!;\n const close = element.querySelector<HTMLButtonElement>(\"[data-close-modal]\")!;\n const dialog = element.querySelector(\"dialog\")!;\n const frame = element.querySelector(\".dialog-frame\")!;\n const status = element.querySelector<HTMLElement>(\"[data-search-status]\");\n const shortcut = open.querySelector(\"kbd\")!;\n if (/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {\n shortcut.querySelector(\"kbd\")!.textContent = \"⌘\";\n open.setAttribute(\"aria-keyshortcuts\", \"Meta+K\");\n }\n delete shortcut.dataset.pending;\n let initialized: Promise<void> | undefined;\n let returnFocus: HTMLElement | null = null;\n const initialize = () => {\n if (development) return Promise.resolve();\n return (initialized ??= (async () => {\n // @ts-expect-error Pagefind publishes no declarations for its default UI.\n const { PagefindUI } = await import(\"@pagefind/default-ui\");\n const translations = JSON.parse(element.dataset.translations ?? \"{}\");\n const format = (url: string) =>\n element.hasAttribute(\"data-strip-trailing-slash\")\n ? url.replace(/(.)\\/(#.*)?$/, \"$1$2\")\n : url;\n new PagefindUI({\n ...options,\n element: \"#cookbook__search\",\n baseUrl: base,\n bundlePath: `${base.replace(/\\/$/, \"\")}/pagefind/`,\n showImages: false,\n showSubResults: true,\n translations,\n processResult(result: SearchResult) {\n result = options.processResult?.(result) ?? result;\n result.url = format(result.url);\n result.sub_results?.forEach((child) => {\n child.url = format(child.url);\n });\n return result;\n },\n });\n })().catch((error: unknown) => {\n initialized = undefined;\n if (status) {\n status.hidden = false;\n // Reuse the localized Pagefind error, with a plain fallback.\n const translations = JSON.parse(element.dataset.translations ?? \"{}\");\n status.textContent =\n translations.error ?? \"Search could not load. Close and try again.\";\n }\n throw error;\n }));\n };\n const show = async () => {\n if (dialog.open) return;\n returnFocus =\n document.activeElement instanceof HTMLElement\n ? document.activeElement\n : open;\n dialog.showModal();\n document.body.setAttribute(\"data-search-modal-open\", \"\");\n if (!development && status) status.hidden = true;\n try {\n await initialize();\n // Pagefind mounts synchronously. Only focus if the user has not closed it.\n if (dialog.open) element.querySelector(\"input\")?.focus();\n } catch {\n /* Keep the close control usable when the search bundle fails. */\n }\n };\n open.addEventListener(\"click\", () => {\n void show();\n });\n open.disabled = false;\n close.addEventListener(\"click\", () => dialog.close());\n dialog.addEventListener(\"click\", (event) => {\n if (!(event.target instanceof Element)) return;\n if (event.target.closest(\"a[href]\") || !frame.contains(event.target))\n dialog.close();\n });\n dialog.addEventListener(\"close\", () => {\n document.body.removeAttribute(\"data-search-modal-open\");\n returnFocus?.focus();\n });\n window.addEventListener(\"keydown\", (event) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"k\") {\n event.preventDefault();\n if (dialog.open) dialog.close();\n else void show();\n }\n });\n}\n"],"mappings":";;AAMA,SAAgB,iBACd,SACA,SACA,aACA,MACM;CACN,IAAI,QAAQ,QAAQ,aAAa;CACjC,QAAQ,QAAQ,cAAc;CAC9B,MAAM,OAAO,QAAQ,cAAiC,mBAAmB;CACzE,MAAM,QAAQ,QAAQ,cAAiC,oBAAoB;CAC3E,MAAM,SAAS,QAAQ,cAAc,QAAQ;CAC7C,MAAM,QAAQ,QAAQ,cAAc,eAAe;CACnD,MAAM,SAAS,QAAQ,cAA2B,sBAAsB;CACxE,MAAM,WAAW,KAAK,cAAc,KAAK;CACzC,IAAI,0BAA0B,KAAK,UAAU,QAAQ,GAAG;EACtD,SAAS,cAAc,KAAK,CAAC,CAAE,cAAc;EAC7C,KAAK,aAAa,qBAAqB,QAAQ;CACjD;CACA,OAAO,SAAS,QAAQ;CACxB,IAAI;CACJ,IAAI,cAAkC;CACtC,MAAM,mBAAmB;EACvB,IAAI,aAAa,OAAO,QAAQ,QAAQ;EACxC,OAAQ,iBAAiB,YAAY;GAEnC,MAAM,EAAE,eAAe,MAAM,OAAO;GACpC,MAAM,eAAe,KAAK,MAAM,QAAQ,QAAQ,gBAAgB,IAAI;GACpE,MAAM,UAAU,QACd,QAAQ,aAAa,2BAA2B,IAC5C,IAAI,QAAQ,gBAAgB,MAAM,IAClC;GACN,IAAI,WAAW;IACb,GAAG;IACH,SAAS;IACT,SAAS;IACT,YAAY,GAAG,KAAK,QAAQ,OAAO,EAAE,EAAE;IACvC,YAAY;IACZ,gBAAgB;IAChB;IACA,cAAc,QAAsB;KAClC,SAAS,QAAQ,gBAAgB,MAAM,KAAK;KAC5C,OAAO,MAAM,OAAO,OAAO,GAAG;KAC9B,OAAO,aAAa,SAAS,UAAU;MACrC,MAAM,MAAM,OAAO,MAAM,GAAG;KAC9B,CAAC;KACD,OAAO;IACT;GACF,CAAC;EACH,EAAA,CAAG,CAAC,CAAC,OAAO,UAAmB;GAC7B,cAAc,KAAA;GACd,IAAI,QAAQ;IACV,OAAO,SAAS;IAEhB,MAAM,eAAe,KAAK,MAAM,QAAQ,QAAQ,gBAAgB,IAAI;IACpE,OAAO,cACL,aAAa,SAAS;GAC1B;GACA,MAAM;EACR,CAAC;CACH;CACA,MAAM,OAAO,YAAY;EACvB,IAAI,OAAO,MAAM;EACjB,cACE,SAAS,yBAAyB,cAC9B,SAAS,gBACT;EACN,OAAO,UAAU;EACjB,SAAS,KAAK,aAAa,0BAA0B,EAAE;EACvD,IAAI,CAAC,eAAe,QAAQ,OAAO,SAAS;EAC5C,IAAI;GACF,MAAM,WAAW;GAEjB,IAAI,OAAO,MAAM,QAAQ,cAAc,OAAO,CAAC,EAAE,MAAM;EACzD,QAAQ,CAER;CACF;CACA,KAAK,iBAAiB,eAAe;EACnC,KAAU;CACZ,CAAC;CACD,KAAK,WAAW;CAChB,MAAM,iBAAiB,eAAe,OAAO,MAAM,CAAC;CACpD,OAAO,iBAAiB,UAAU,UAAU;EAC1C,IAAI,EAAE,MAAM,kBAAkB,UAAU;EACxC,IAAI,MAAM,OAAO,QAAQ,SAAS,KAAK,CAAC,MAAM,SAAS,MAAM,MAAM,GACjE,OAAO,MAAM;CACjB,CAAC;CACD,OAAO,iBAAiB,eAAe;EACrC,SAAS,KAAK,gBAAgB,wBAAwB;EACtD,aAAa,MAAM;CACrB,CAAC;CACD,OAAO,iBAAiB,YAAY,UAAU;EAC5C,KAAK,MAAM,WAAW,MAAM,YAAY,MAAM,IAAI,YAAY,MAAM,KAAK;GACvE,MAAM,eAAe;GACrB,IAAI,OAAO,MAAM,OAAO,MAAM;QACzB,KAAU;EACjB;CACF,CAAC;AACH"}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,93 @@
1
+ //#region src/client/sidebar.ts
2
+ var CookbookSidebar = class extends HTMLElement {
3
+ #listeners;
4
+ connectedCallback() {
5
+ this.#listeners?.abort();
6
+ this.#listeners = new AbortController();
7
+ const { signal } = this.#listeners;
8
+ const key = this.dataset.storageKey;
9
+ if (!key) return;
10
+ const desktop = matchMedia("(min-width: 50rem)");
11
+ const groups = this.querySelectorAll("details[data-sidebar-group]");
12
+ const scroller = this.closest("#cookbook__sidebar");
13
+ let saved = {};
14
+ try {
15
+ const value = JSON.parse(sessionStorage.getItem(key) || "{}");
16
+ if (value && typeof value === "object" && !Array.isArray(value)) saved = Object.fromEntries(Object.entries(value).filter(([, open]) => typeof open === "boolean"));
17
+ } catch {}
18
+ for (const group of groups) {
19
+ const savedOpen = saved[group.dataset.sidebarGroup];
20
+ if (group.querySelector(":scope > ul [aria-current=\"page\"]")) group.open = true;
21
+ else if (typeof savedOpen === "boolean") group.open = savedOpen;
22
+ }
23
+ try {
24
+ if (scroller && desktop.matches) {
25
+ const scroll = Number(sessionStorage.getItem(`${key}:scroll`));
26
+ if (Number.isFinite(scroll) && scroll >= 0) scroller.scrollTop = scroll;
27
+ }
28
+ } catch {}
29
+ for (const group of groups) {
30
+ const summary = group.querySelector(":scope > summary");
31
+ const link = summary?.querySelector("a[data-cookbook-group-link]");
32
+ let previousOpen = group.open;
33
+ const syncExpanded = () => link?.setAttribute("aria-expanded", String(group.open));
34
+ const saveOpen = () => {
35
+ previousOpen = group.open;
36
+ saved[group.dataset.sidebarGroup] = group.open;
37
+ try {
38
+ sessionStorage.setItem(key, JSON.stringify(saved));
39
+ } catch {}
40
+ };
41
+ syncExpanded();
42
+ window.addEventListener("pageshow", (event) => {
43
+ if (!event.persisted || !group.querySelector(":scope > ul [aria-current=\"page\"]")) return;
44
+ previousOpen = true;
45
+ group.open = true;
46
+ syncExpanded();
47
+ }, { signal });
48
+ if (link && summary) {
49
+ summary.addEventListener("click", (event) => {
50
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
51
+ if (!(event.target instanceof Node) || !link.contains(event.target)) {
52
+ event.preventDefault();
53
+ link.click();
54
+ return;
55
+ }
56
+ const current = link.getAttribute("aria-current") === "page";
57
+ group.open = current ? !group.open : true;
58
+ syncExpanded();
59
+ saveOpen();
60
+ if (current) event.preventDefault();
61
+ }, { signal });
62
+ link.addEventListener("keydown", (event) => {
63
+ if (event.key === " " && !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey) {
64
+ event.preventDefault();
65
+ if (!event.repeat) link.click();
66
+ }
67
+ }, { signal });
68
+ }
69
+ group.addEventListener("toggle", () => {
70
+ syncExpanded();
71
+ if (group.open === previousOpen) return;
72
+ saveOpen();
73
+ }, { signal });
74
+ }
75
+ scroller?.addEventListener("scroll", () => {
76
+ if (!desktop.matches) return;
77
+ try {
78
+ sessionStorage.setItem(`${key}:scroll`, String(scroller.scrollTop));
79
+ } catch {}
80
+ }, {
81
+ passive: true,
82
+ signal
83
+ });
84
+ }
85
+ disconnectedCallback() {
86
+ this.#listeners?.abort();
87
+ }
88
+ };
89
+ if (!customElements.get("cookbook-sidebar")) customElements.define("cookbook-sidebar", CookbookSidebar);
90
+ //#endregion
91
+ export {};
92
+
93
+ //# sourceMappingURL=sidebar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar.js","names":[],"sources":["../../src/client/sidebar.ts"],"sourcesContent":["export {};\n\nclass CookbookSidebar extends HTMLElement {\n #listeners: AbortController | undefined;\n\n connectedCallback() {\n this.#listeners?.abort();\n this.#listeners = new AbortController();\n const { signal } = this.#listeners;\n const key = this.dataset.storageKey;\n if (!key) return;\n const desktop = matchMedia(\"(min-width: 50rem)\");\n const groups = this.querySelectorAll<HTMLDetailsElement>(\n \"details[data-sidebar-group]\",\n );\n const scroller = this.closest<HTMLElement>(\"#cookbook__sidebar\");\n let saved: Record<string, boolean> = {};\n try {\n const value: unknown = JSON.parse(sessionStorage.getItem(key) || \"{}\");\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n saved = Object.fromEntries(\n Object.entries(value).filter(([, open]) => typeof open === \"boolean\"),\n );\n }\n } catch {\n // Native disclosures still work when session storage is unavailable.\n }\n for (const group of groups) {\n const savedOpen = saved[group.dataset.sidebarGroup!];\n // Always reveal the current page, even if its branch was closed before.\n if (group.querySelector(':scope > ul [aria-current=\"page\"]'))\n group.open = true;\n else if (typeof savedOpen === \"boolean\") group.open = savedOpen;\n }\n try {\n if (scroller && desktop.matches) {\n const scroll = Number(sessionStorage.getItem(`${key}:scroll`));\n if (Number.isFinite(scroll) && scroll >= 0) scroller.scrollTop = scroll;\n }\n } catch {\n // Native disclosures still work when session storage is unavailable.\n }\n for (const group of groups) {\n const summary = group.querySelector<HTMLElement>(\":scope > summary\");\n const link = summary?.querySelector<HTMLAnchorElement>(\n \"a[data-cookbook-group-link]\",\n );\n let previousOpen = group.open;\n const syncExpanded = () =>\n link?.setAttribute(\"aria-expanded\", String(group.open));\n const saveOpen = () => {\n previousOpen = group.open;\n saved[group.dataset.sidebarGroup!] = group.open;\n try {\n sessionStorage.setItem(key, JSON.stringify(saved));\n } catch {\n // Remembering expansion is optional.\n }\n };\n syncExpanded();\n window.addEventListener(\n \"pageshow\",\n (event) => {\n if (\n !event.persisted ||\n !group.querySelector(':scope > ul [aria-current=\"page\"]')\n )\n return;\n // Back/Forward may restore the old DOM without reconnecting this\n // element. Reveal its current page without changing saved choices.\n previousOpen = true;\n group.open = true;\n syncExpanded();\n },\n { signal },\n );\n if (link && summary) {\n summary.addEventListener(\n \"click\",\n (event) => {\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n )\n return;\n // The entire row follows the link, including the summary's edges.\n if (\n !(event.target instanceof Node) ||\n !link.contains(event.target)\n ) {\n event.preventDefault();\n link.click();\n return;\n }\n const current = link.getAttribute(\"aria-current\") === \"page\";\n group.open = current ? !group.open : true;\n syncExpanded();\n // Persist before navigation: the native toggle event is queued and\n // may never run before the browser unloads this document.\n saveOpen();\n if (current) event.preventDefault();\n },\n { signal },\n );\n link.addEventListener(\n \"keydown\",\n (event) => {\n if (\n event.key === \" \" &&\n !event.metaKey &&\n !event.ctrlKey &&\n !event.altKey &&\n !event.shiftKey\n ) {\n event.preventDefault();\n if (!event.repeat) link.click();\n }\n },\n { signal },\n );\n }\n group.addEventListener(\n \"toggle\",\n () => {\n syncExpanded();\n // Ignore queued events caused by parsing or restoring the markup.\n if (group.open === previousOpen) return;\n saveOpen();\n },\n { signal },\n );\n }\n scroller?.addEventListener(\n \"scroll\",\n () => {\n if (!desktop.matches) return;\n try {\n sessionStorage.setItem(`${key}:scroll`, String(scroller.scrollTop));\n } catch {\n // Scroll restoration is optional too.\n }\n },\n { passive: true, signal },\n );\n }\n\n disconnectedCallback() {\n this.#listeners?.abort();\n }\n}\nif (!customElements.get(\"cookbook-sidebar\")) {\n customElements.define(\"cookbook-sidebar\", CookbookSidebar);\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ //#region src/client/tabs.d.ts
2
+ /** Enhance progressively: every panel remains readable when JavaScript is disabled. */
3
+ declare function initializeTabs(): void;
4
+ //#endregion
5
+ export { initializeTabs };
6
+ //# sourceMappingURL=tabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.d.ts","names":[],"sources":["../../src/client/tabs.ts"],"mappings":";;iBAGgB"}
@@ -0,0 +1,64 @@
1
+ //#region src/client/tabs.ts
2
+ let nextId = 0;
3
+ /** Enhance progressively: every panel remains readable when JavaScript is disabled. */
4
+ function initializeTabs() {
5
+ for (const root of document.querySelectorAll("[data-cookbook-tabs]")) {
6
+ if (root.dataset.tabsReady) continue;
7
+ const list = root.querySelector(":scope > [data-tabs-list]");
8
+ const panels = [...root.querySelectorAll(":scope > [data-tab-panel]")];
9
+ if (!list || panels.length === 0) continue;
10
+ root.dataset.tabsReady = "true";
11
+ let prefix;
12
+ do
13
+ prefix = `cookbook-tabs-${++nextId}`;
14
+ while (document.getElementById(`${prefix}-tab-0`));
15
+ list.setAttribute("role", "tablist");
16
+ list.setAttribute("aria-label", root.getAttribute("aria-label") ?? "Examples");
17
+ const buttons = panels.map((panel, index) => {
18
+ const button = document.createElement("button");
19
+ button.type = "button";
20
+ button.id = `${prefix}-tab-${index}`;
21
+ button.textContent = panel.dataset.label ?? `Tab ${index + 1}`;
22
+ button.setAttribute("role", "tab");
23
+ panel.id = `${prefix}-panel-${index}`;
24
+ panel.setAttribute("role", "tabpanel");
25
+ panel.setAttribute("aria-labelledby", button.id);
26
+ panel.tabIndex = 0;
27
+ button.setAttribute("aria-controls", panel.id);
28
+ const heading = panel.querySelector(":scope > [data-tab-heading]");
29
+ if (heading) heading.hidden = true;
30
+ list.append(button);
31
+ return button;
32
+ });
33
+ const activate = (index, focus = false) => {
34
+ for (const [position, button] of buttons.entries()) {
35
+ const selected = position === index;
36
+ button.setAttribute("aria-selected", String(selected));
37
+ button.tabIndex = selected ? 0 : -1;
38
+ panels[position].hidden = !selected;
39
+ }
40
+ if (focus) buttons[index]?.focus();
41
+ };
42
+ buttons.forEach((button, index) => {
43
+ button.addEventListener("click", () => activate(index));
44
+ button.addEventListener("keydown", (event) => {
45
+ const rtl = getComputedStyle(root).direction === "rtl";
46
+ const forward = rtl ? "ArrowLeft" : "ArrowRight";
47
+ const backward = rtl ? "ArrowRight" : "ArrowLeft";
48
+ let target;
49
+ if (event.key === forward) target = (index + 1) % buttons.length;
50
+ else if (event.key === backward) target = (index + buttons.length - 1) % buttons.length;
51
+ else if (event.key === "Home") target = 0;
52
+ else if (event.key === "End") target = buttons.length - 1;
53
+ else return;
54
+ event.preventDefault();
55
+ activate(target, true);
56
+ });
57
+ });
58
+ activate(0);
59
+ }
60
+ }
61
+ //#endregion
62
+ export { initializeTabs };
63
+
64
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.js","names":[],"sources":["../../src/client/tabs.ts"],"sourcesContent":["let nextId = 0;\n\n/** Enhance progressively: every panel remains readable when JavaScript is disabled. */\nexport function initializeTabs(): void {\n for (const root of document.querySelectorAll<HTMLElement>(\n \"[data-cookbook-tabs]\",\n )) {\n if (root.dataset.tabsReady) continue;\n const list = root.querySelector<HTMLElement>(\":scope > [data-tabs-list]\");\n const panels = [\n ...root.querySelectorAll<HTMLElement>(\":scope > [data-tab-panel]\"),\n ];\n if (!list || panels.length === 0) continue;\n root.dataset.tabsReady = \"true\";\n let prefix: string;\n do {\n prefix = `cookbook-tabs-${++nextId}`;\n } while (document.getElementById(`${prefix}-tab-0`));\n list.setAttribute(\"role\", \"tablist\");\n list.setAttribute(\n \"aria-label\",\n root.getAttribute(\"aria-label\") ?? \"Examples\",\n );\n const buttons = panels.map((panel, index) => {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.id = `${prefix}-tab-${index}`;\n button.textContent = panel.dataset.label ?? `Tab ${index + 1}`;\n button.setAttribute(\"role\", \"tab\");\n panel.id = `${prefix}-panel-${index}`;\n panel.setAttribute(\"role\", \"tabpanel\");\n panel.setAttribute(\"aria-labelledby\", button.id);\n panel.tabIndex = 0;\n button.setAttribute(\"aria-controls\", panel.id);\n const heading = panel.querySelector<HTMLElement>(\n \":scope > [data-tab-heading]\",\n );\n if (heading) heading.hidden = true;\n list.append(button);\n return button;\n });\n const activate = (index: number, focus = false) => {\n for (const [position, button] of buttons.entries()) {\n const selected = position === index;\n button.setAttribute(\"aria-selected\", String(selected));\n button.tabIndex = selected ? 0 : -1;\n panels[position]!.hidden = !selected;\n }\n if (focus) buttons[index]?.focus();\n };\n buttons.forEach((button, index) => {\n button.addEventListener(\"click\", () => activate(index));\n button.addEventListener(\"keydown\", (event) => {\n const rtl = getComputedStyle(root).direction === \"rtl\";\n const forward = rtl ? \"ArrowLeft\" : \"ArrowRight\";\n const backward = rtl ? \"ArrowRight\" : \"ArrowLeft\";\n let target: number;\n if (event.key === forward) target = (index + 1) % buttons.length;\n else if (event.key === backward)\n target = (index + buttons.length - 1) % buttons.length;\n else if (event.key === \"Home\") target = 0;\n else if (event.key === \"End\") target = buttons.length - 1;\n else return;\n event.preventDefault();\n activate(target, true);\n });\n });\n activate(0);\n }\n}\n"],"mappings":";AAAA,IAAI,SAAS;;AAGb,SAAgB,iBAAuB;CACrC,KAAK,MAAM,QAAQ,SAAS,iBAC1B,sBACF,GAAG;EACD,IAAI,KAAK,QAAQ,WAAW;EAC5B,MAAM,OAAO,KAAK,cAA2B,2BAA2B;EACxE,MAAM,SAAS,CACb,GAAG,KAAK,iBAA8B,2BAA2B,CACnE;EACA,IAAI,CAAC,QAAQ,OAAO,WAAW,GAAG;EAClC,KAAK,QAAQ,YAAY;EACzB,IAAI;EACJ;GACE,SAAS,iBAAiB,EAAE;SACrB,SAAS,eAAe,GAAG,OAAO,OAAO;EAClD,KAAK,aAAa,QAAQ,SAAS;EACnC,KAAK,aACH,cACA,KAAK,aAAa,YAAY,KAAK,UACrC;EACA,MAAM,UAAU,OAAO,KAAK,OAAO,UAAU;GAC3C,MAAM,SAAS,SAAS,cAAc,QAAQ;GAC9C,OAAO,OAAO;GACd,OAAO,KAAK,GAAG,OAAO,OAAO;GAC7B,OAAO,cAAc,MAAM,QAAQ,SAAS,OAAO,QAAQ;GAC3D,OAAO,aAAa,QAAQ,KAAK;GACjC,MAAM,KAAK,GAAG,OAAO,SAAS;GAC9B,MAAM,aAAa,QAAQ,UAAU;GACrC,MAAM,aAAa,mBAAmB,OAAO,EAAE;GAC/C,MAAM,WAAW;GACjB,OAAO,aAAa,iBAAiB,MAAM,EAAE;GAC7C,MAAM,UAAU,MAAM,cACpB,6BACF;GACA,IAAI,SAAS,QAAQ,SAAS;GAC9B,KAAK,OAAO,MAAM;GAClB,OAAO;EACT,CAAC;EACD,MAAM,YAAY,OAAe,QAAQ,UAAU;GACjD,KAAK,MAAM,CAAC,UAAU,WAAW,QAAQ,QAAQ,GAAG;IAClD,MAAM,WAAW,aAAa;IAC9B,OAAO,aAAa,iBAAiB,OAAO,QAAQ,CAAC;IACrD,OAAO,WAAW,WAAW,IAAI;IACjC,OAAO,SAAS,CAAE,SAAS,CAAC;GAC9B;GACA,IAAI,OAAO,QAAQ,MAAM,EAAE,MAAM;EACnC;EACA,QAAQ,SAAS,QAAQ,UAAU;GACjC,OAAO,iBAAiB,eAAe,SAAS,KAAK,CAAC;GACtD,OAAO,iBAAiB,YAAY,UAAU;IAC5C,MAAM,MAAM,iBAAiB,IAAI,CAAC,CAAC,cAAc;IACjD,MAAM,UAAU,MAAM,cAAc;IACpC,MAAM,WAAW,MAAM,eAAe;IACtC,IAAI;IACJ,IAAI,MAAM,QAAQ,SAAS,UAAU,QAAQ,KAAK,QAAQ;SACrD,IAAI,MAAM,QAAQ,UACrB,UAAU,QAAQ,QAAQ,SAAS,KAAK,QAAQ;SAC7C,IAAI,MAAM,QAAQ,QAAQ,SAAS;SACnC,IAAI,MAAM,QAAQ,OAAO,SAAS,QAAQ,SAAS;SACnD;IACL,MAAM,eAAe;IACrB,SAAS,QAAQ,IAAI;GACvB,CAAC;EACH,CAAC;EACD,SAAS,CAAC;CACZ;AACF"}
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { CalloutRoot } from "./TastyComponents.js";
3
+ import type { CalloutProps as Props } from "./props.js";
4
+ const {
5
+ kind = "note",
6
+ title = { note: "Note", tip: "Tip", caution: "Caution", danger: "Danger" }[
7
+ kind
8
+ ],
9
+ } = Astro.props;
10
+ ---
11
+
12
+ <CalloutRoot data-kind={kind} aria-label={title}>
13
+ <CalloutRoot.Title>{title}</CalloutRoot.Title>
14
+ <CalloutRoot.Body><slot /></CalloutRoot.Body>
15
+ </CalloutRoot>
@@ -0,0 +1,13 @@
1
+ ---
2
+ import { CardLink, CardRoot } from "./TastyComponents.js";
3
+
4
+ import type { CardProps as Props } from "./props.js";
5
+
6
+ const { title, href } = Astro.props;
7
+ const Root = href ? CardLink : CardRoot;
8
+ ---
9
+
10
+ <Root className="td-card" href={href} data-tasty-anatomy="Card">
11
+ {title && <h3 data-tasty-anatomy="CardTitle">{title}</h3>}
12
+ <div data-tasty-anatomy="CardBody"><slot /></div>
13
+ </Root>
@@ -0,0 +1,47 @@
1
+ ---
2
+ import { Code } from "astro:components";
3
+ import Tabs from "./Tabs.astro";
4
+ import Tab from "./Tab.astro";
5
+ import { CodeGroupRoot } from "./TastyComponents.js";
6
+ import { codeGroupHighlighting } from "../theme/shiki-theme.js";
7
+ import type { CodeGroupProps as Props } from "./props.js";
8
+ const { label = "Code examples", items } = Astro.props;
9
+ const highlight = codeGroupHighlighting();
10
+ ---
11
+
12
+ <CodeGroupRoot data-tasty-anatomy="CodeGroup">
13
+ <Tabs label={label}>
14
+ {
15
+ items.map((item) => (
16
+ <Tab label={item.label}>
17
+ {item.language && (
18
+ <CodeGroupRoot.Caption>{item.language}</CodeGroupRoot.Caption>
19
+ )}
20
+ <cookbook-code-block
21
+ class="td-code-block"
22
+ data-tasty-anatomy="MarkdownCodeBlock"
23
+ >
24
+ <Code
25
+ code={item.code}
26
+ lang={item.language ?? "text"}
27
+ theme={highlight.theme}
28
+ transformers={highlight.transformers}
29
+ embeddedLangs={highlight.langs}
30
+ />
31
+ <button
32
+ type="button"
33
+ data-copy-code
34
+ aria-label="Copy code"
35
+ title="Copy code"
36
+ >
37
+ <span data-copy-icon aria-hidden="true" />
38
+ </button>
39
+ </cookbook-code-block>
40
+ </Tab>
41
+ ))
42
+ }
43
+ </Tabs>
44
+ </CodeGroupRoot>
45
+ <script>
46
+ import "../client/code-copy.js";
47
+ </script>
@@ -0,0 +1,29 @@
1
+ ---
2
+ interface Props {
3
+ level: "normal" | "more";
4
+ }
5
+
6
+ const { level } = Astro.props;
7
+ ---
8
+
9
+ <svg
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ viewBox="0 0 24 24"
12
+ aria-hidden="true"
13
+ focusable="false"
14
+ >
15
+ <circle
16
+ cx="12"
17
+ cy="12"
18
+ r="8"
19
+ fill="none"
20
+ stroke="currentColor"
21
+ stroke-width="2"></circle>
22
+ {
23
+ level === "more" ? (
24
+ <path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor" />
25
+ ) : (
26
+ <path d="M12 4v16" fill="none" stroke="currentColor" stroke-width="2" />
27
+ )
28
+ }
29
+ </svg>
@@ -0,0 +1,11 @@
1
+ ---
2
+ const { editUrl } = Astro.locals.cookbookRoute;
3
+ ---
4
+
5
+ {
6
+ editUrl && (
7
+ <a href={editUrl} class="cookbook-print-hidden">
8
+ {Astro.locals.t("page.editLink")}
9
+ </a>
10
+ )
11
+ }