@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andrey Yamanov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # @tenphi/renderer
2
+
3
+ The Astro renderer for Cookbook. The default export is a complete integration;
4
+ consumers normally use it through `@tenphi/cookbook`.
5
+
6
+ The integration uses one graph-backed route pipeline for convention, explicit,
7
+ and locked-package sources. It targets Astro 7.3 and includes a Popover-based
8
+ mobile sidebar. Application-owned Astro content
9
+ collections remain independent.
10
+
11
+ `@tenphi/renderer/styling` exports `tasty`, `useGlobalStyles`, `mergeStyles`,
12
+ the `Styles` type, and the `defineComponent` and `resolveComponentStyles`
13
+ helpers for merging `theme.styles` into custom components. The facade exposes
14
+ the same API through `@tenphi/cookbook/styling`.
15
+
16
+ `@tenphi/renderer/eslint-plugin` exports the default Tasty ESLint plugin,
17
+ `recommended` and `strict` rule maps, its configuration types, and a
18
+ `validationConfig` preset covering Cookbook's theme names and styling helpers.
19
+ Use `extends: "@tenphi/renderer"` in `tasty.config.ts` to inherit this preset
20
+ and list only your additional tokens, states, or presets.
21
+ It matches `@tenphi/cookbook/eslint-plugin`; see
22
+ [Linting custom styles](https://cookbook.tenphi.me/theme-and-components/#linting-custom-styles)
23
+ for ESLint and oxlint setup.
@@ -0,0 +1,12 @@
1
+ //#region src/client/appearance.d.ts
2
+ type Theme = "light" | "dark" | "system";
3
+ type Contrast = "normal" | "more" | "system";
4
+ type Appearance = {
5
+ theme?: Theme;
6
+ contrast?: Contrast;
7
+ };
8
+ declare function applyAppearance(value: Appearance): void;
9
+ declare function saveAppearance(value: Appearance): void;
10
+ //#endregion
11
+ export { applyAppearance, saveAppearance };
12
+ //# sourceMappingURL=appearance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appearance.d.ts","names":[],"sources":["../../src/client/appearance.ts"],"mappings":";KAEK;KACA;KACA;EAAe,QAAQ;EAAO,WAAW;;iBAK9B,gBAAgB,OAAO;iBASvB,eAAe,OAAO"}
@@ -0,0 +1,66 @@
1
+ //#region src/client/appearance.ts
2
+ const STORAGE_KEY = "cookbook-appearance";
3
+ const themes = /* @__PURE__ */ new Set([
4
+ "light",
5
+ "dark",
6
+ "system"
7
+ ]);
8
+ const contrasts = /* @__PURE__ */ new Set([
9
+ "normal",
10
+ "more",
11
+ "system"
12
+ ]);
13
+ function applyAppearance(value) {
14
+ const root = document.documentElement;
15
+ if (value.theme && value.theme !== "system") root.dataset.theme = value.theme;
16
+ else delete root.dataset.theme;
17
+ if (value.contrast && value.contrast !== "system") root.dataset.contrast = value.contrast;
18
+ else delete root.dataset.contrast;
19
+ }
20
+ function saveAppearance(value) {
21
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
22
+ applyAppearance(value);
23
+ }
24
+ try {
25
+ const stored = localStorage.getItem(STORAGE_KEY);
26
+ if (stored) applyAppearance(normalizeAppearance(JSON.parse(stored)));
27
+ } catch {}
28
+ const theme = document.querySelector("[data-docs-theme]");
29
+ const contrast = document.querySelector("[data-docs-contrast]");
30
+ let current = {
31
+ theme: "system",
32
+ contrast: "system"
33
+ };
34
+ try {
35
+ const stored = localStorage.getItem(STORAGE_KEY);
36
+ if (stored) current = normalizeAppearance(JSON.parse(stored));
37
+ } catch {}
38
+ if (theme) theme.value = current.theme ?? "system";
39
+ if (contrast) contrast.value = current.contrast ?? "system";
40
+ const update = () => {
41
+ current = {
42
+ theme: normalizeTheme(theme?.value),
43
+ contrast: normalizeContrast(contrast?.value)
44
+ };
45
+ saveAppearance(current);
46
+ };
47
+ theme?.addEventListener("change", update);
48
+ contrast?.addEventListener("change", update);
49
+ function normalizeAppearance(value) {
50
+ if (!value || typeof value !== "object") return {};
51
+ const record = value;
52
+ return {
53
+ theme: normalizeTheme(record.theme),
54
+ contrast: normalizeContrast(record.contrast)
55
+ };
56
+ }
57
+ function normalizeTheme(value) {
58
+ return typeof value === "string" && themes.has(value) ? value : "system";
59
+ }
60
+ function normalizeContrast(value) {
61
+ return typeof value === "string" && contrasts.has(value) ? value : "system";
62
+ }
63
+ //#endregion
64
+ export { applyAppearance, saveAppearance };
65
+
66
+ //# sourceMappingURL=appearance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appearance.js","names":[],"sources":["../../src/client/appearance.ts"],"sourcesContent":["const STORAGE_KEY = \"cookbook-appearance\";\n\ntype Theme = \"light\" | \"dark\" | \"system\";\ntype Contrast = \"normal\" | \"more\" | \"system\";\ntype Appearance = { theme?: Theme; contrast?: Contrast };\n\nconst themes = new Set([\"light\", \"dark\", \"system\"]);\nconst contrasts = new Set([\"normal\", \"more\", \"system\"]);\n\nexport function applyAppearance(value: Appearance): void {\n const root = document.documentElement;\n if (value.theme && value.theme !== \"system\") root.dataset.theme = value.theme;\n else delete root.dataset.theme;\n if (value.contrast && value.contrast !== \"system\")\n root.dataset.contrast = value.contrast;\n else delete root.dataset.contrast;\n}\n\nexport function saveAppearance(value: Appearance): void {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(value));\n applyAppearance(value);\n}\n\ntry {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) applyAppearance(normalizeAppearance(JSON.parse(stored)));\n} catch {\n // Storage may be unavailable; CSS media queries remain the fallback.\n}\n\nconst theme = document.querySelector<HTMLSelectElement>(\"[data-docs-theme]\");\nconst contrast = document.querySelector<HTMLSelectElement>(\n \"[data-docs-contrast]\",\n);\nlet current: Appearance = { theme: \"system\", contrast: \"system\" };\ntry {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) current = normalizeAppearance(JSON.parse(stored));\n} catch {\n // The controls retain system defaults when storage is unavailable.\n}\nif (theme) theme.value = current.theme ?? \"system\";\nif (contrast) contrast.value = current.contrast ?? \"system\";\nconst update = () => {\n current = {\n theme: normalizeTheme(theme?.value),\n contrast: normalizeContrast(contrast?.value),\n };\n saveAppearance(current);\n};\ntheme?.addEventListener(\"change\", update);\ncontrast?.addEventListener(\"change\", update);\n\nfunction normalizeAppearance(value: unknown): Appearance {\n if (!value || typeof value !== \"object\") return {};\n const record = value as Record<string, unknown>;\n return {\n theme: normalizeTheme(record.theme),\n contrast: normalizeContrast(record.contrast),\n };\n}\n\nfunction normalizeTheme(value: unknown): Theme {\n return typeof value === \"string\" && themes.has(value)\n ? (value as Theme)\n : \"system\";\n}\n\nfunction normalizeContrast(value: unknown): Contrast {\n return typeof value === \"string\" && contrasts.has(value)\n ? (value as Contrast)\n : \"system\";\n}\n"],"mappings":";AAAA,MAAM,cAAc;AAMpB,MAAM,yBAAS,IAAI,IAAI;CAAC;CAAS;CAAQ;AAAQ,CAAC;AAClD,MAAM,4BAAY,IAAI,IAAI;CAAC;CAAU;CAAQ;AAAQ,CAAC;AAEtD,SAAgB,gBAAgB,OAAyB;CACvD,MAAM,OAAO,SAAS;CACtB,IAAI,MAAM,SAAS,MAAM,UAAU,UAAU,KAAK,QAAQ,QAAQ,MAAM;MACnE,OAAO,KAAK,QAAQ;CACzB,IAAI,MAAM,YAAY,MAAM,aAAa,UACvC,KAAK,QAAQ,WAAW,MAAM;MAC3B,OAAO,KAAK,QAAQ;AAC3B;AAEA,SAAgB,eAAe,OAAyB;CACtD,aAAa,QAAQ,aAAa,KAAK,UAAU,KAAK,CAAC;CACvD,gBAAgB,KAAK;AACvB;AAEA,IAAI;CACF,MAAM,SAAS,aAAa,QAAQ,WAAW;CAC/C,IAAI,QAAQ,gBAAgB,oBAAoB,KAAK,MAAM,MAAM,CAAC,CAAC;AACrE,QAAQ,CAER;AAEA,MAAM,QAAQ,SAAS,cAAiC,mBAAmB;AAC3E,MAAM,WAAW,SAAS,cACxB,sBACF;AACA,IAAI,UAAsB;CAAE,OAAO;CAAU,UAAU;AAAS;AAChE,IAAI;CACF,MAAM,SAAS,aAAa,QAAQ,WAAW;CAC/C,IAAI,QAAQ,UAAU,oBAAoB,KAAK,MAAM,MAAM,CAAC;AAC9D,QAAQ,CAER;AACA,IAAI,OAAO,MAAM,QAAQ,QAAQ,SAAS;AAC1C,IAAI,UAAU,SAAS,QAAQ,QAAQ,YAAY;AACnD,MAAM,eAAe;CACnB,UAAU;EACR,OAAO,eAAe,OAAO,KAAK;EAClC,UAAU,kBAAkB,UAAU,KAAK;CAC7C;CACA,eAAe,OAAO;AACxB;AACA,OAAO,iBAAiB,UAAU,MAAM;AACxC,UAAU,iBAAiB,UAAU,MAAM;AAE3C,SAAS,oBAAoB,OAA4B;CACvD,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO,CAAC;CACjD,MAAM,SAAS;CACf,OAAO;EACL,OAAO,eAAe,OAAO,KAAK;EAClC,UAAU,kBAAkB,OAAO,QAAQ;CAC7C;AACF;AAEA,SAAS,eAAe,OAAuB;CAC7C,OAAO,OAAO,UAAU,YAAY,OAAO,IAAI,KAAK,IAC/C,QACD;AACN;AAEA,SAAS,kBAAkB,OAA0B;CACnD,OAAO,OAAO,UAAU,YAAY,UAAU,IAAI,KAAK,IAClD,QACD;AACN"}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,63 @@
1
+ import { t as message } from "../messages-B_3xUzUb.js";
2
+ //#region src/client/code-copy.ts
3
+ var CookbookCodeBlock = class extends HTMLElement {
4
+ #resetTimer;
5
+ connectedCallback() {
6
+ const button = this.querySelector("[data-copy-code]");
7
+ if (button) {
8
+ button.setAttribute("aria-label", message("copyCode"));
9
+ button.title = message("copyCode");
10
+ }
11
+ this.addEventListener("click", this.#copy);
12
+ }
13
+ disconnectedCallback() {
14
+ this.removeEventListener("click", this.#copy);
15
+ if (this.#resetTimer !== void 0) window.clearTimeout(this.#resetTimer);
16
+ }
17
+ #copy = async (event) => {
18
+ const target = event.target;
19
+ const button = target instanceof Element ? target.closest("[data-copy-code]") : void 0;
20
+ const code = this.querySelector("pre code");
21
+ if (!button || !this.contains(button) || !code) return;
22
+ try {
23
+ await writeClipboard(code);
24
+ } catch {
25
+ button.dataset.copyState = "error";
26
+ button.setAttribute("aria-label", message("codeCopyError"));
27
+ button.title = message("codeCopyError");
28
+ return;
29
+ }
30
+ button.dataset.copyState = "copied";
31
+ button.setAttribute("aria-label", message("codeCopied"));
32
+ button.title = message("codeCopied");
33
+ if (this.#resetTimer !== void 0) window.clearTimeout(this.#resetTimer);
34
+ this.#resetTimer = window.setTimeout(() => {
35
+ delete button.dataset.copyState;
36
+ button.setAttribute("aria-label", message("copyCode"));
37
+ button.title = message("copyCode");
38
+ this.#resetTimer = void 0;
39
+ }, 2e3);
40
+ };
41
+ };
42
+ async function writeClipboard(code) {
43
+ const value = code.textContent ?? "";
44
+ if (navigator.clipboard?.writeText) {
45
+ await navigator.clipboard.writeText(value);
46
+ return;
47
+ }
48
+ const selection = window.getSelection();
49
+ const previousRanges = selection ? Array.from({ length: selection.rangeCount }, (_, index) => selection.getRangeAt(index)) : [];
50
+ const range = document.createRange();
51
+ range.selectNodeContents(code);
52
+ selection?.removeAllRanges();
53
+ selection?.addRange(range);
54
+ const copied = document.execCommand("copy");
55
+ selection?.removeAllRanges();
56
+ for (const previousRange of previousRanges) selection?.addRange(previousRange);
57
+ if (!copied) throw new Error("Clipboard unavailable");
58
+ }
59
+ if (!customElements.get("cookbook-code-block")) customElements.define("cookbook-code-block", CookbookCodeBlock);
60
+ //#endregion
61
+ export {};
62
+
63
+ //# sourceMappingURL=code-copy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-copy.js","names":[],"sources":["../../src/client/code-copy.ts"],"sourcesContent":["import { message } from \"./messages.js\";\n\nclass CookbookCodeBlock extends HTMLElement {\n #resetTimer: number | undefined;\n\n connectedCallback() {\n const button = this.querySelector<HTMLButtonElement>(\"[data-copy-code]\");\n if (button) {\n button.setAttribute(\"aria-label\", message(\"copyCode\"));\n button.title = message(\"copyCode\");\n }\n this.addEventListener(\"click\", this.#copy);\n }\n\n disconnectedCallback() {\n this.removeEventListener(\"click\", this.#copy);\n if (this.#resetTimer !== undefined) {\n window.clearTimeout(this.#resetTimer);\n }\n }\n\n #copy = async (event: MouseEvent) => {\n const target = event.target;\n const button =\n target instanceof Element\n ? target.closest<HTMLButtonElement>(\"[data-copy-code]\")\n : undefined;\n const code = this.querySelector<HTMLElement>(\"pre code\");\n if (!button || !this.contains(button) || !code) return;\n\n try {\n await writeClipboard(code);\n } catch {\n button.dataset.copyState = \"error\";\n button.setAttribute(\"aria-label\", message(\"codeCopyError\"));\n button.title = message(\"codeCopyError\");\n return;\n }\n\n button.dataset.copyState = \"copied\";\n button.setAttribute(\"aria-label\", message(\"codeCopied\"));\n button.title = message(\"codeCopied\");\n\n if (this.#resetTimer !== undefined) {\n window.clearTimeout(this.#resetTimer);\n }\n this.#resetTimer = window.setTimeout(() => {\n delete button.dataset.copyState;\n button.setAttribute(\"aria-label\", message(\"copyCode\"));\n button.title = message(\"copyCode\");\n this.#resetTimer = undefined;\n }, 2_000);\n };\n}\n\nasync function writeClipboard(code: HTMLElement): Promise<void> {\n const value = code.textContent ?? \"\";\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(value);\n return;\n }\n\n const selection = window.getSelection();\n const previousRanges = selection\n ? Array.from({ length: selection.rangeCount }, (_, index) =>\n selection.getRangeAt(index),\n )\n : [];\n const range = document.createRange();\n range.selectNodeContents(code);\n selection?.removeAllRanges();\n selection?.addRange(range);\n const copied = document.execCommand(\"copy\");\n selection?.removeAllRanges();\n for (const previousRange of previousRanges)\n selection?.addRange(previousRange);\n if (!copied) throw new Error(\"Clipboard unavailable\");\n}\n\nif (!customElements.get(\"cookbook-code-block\")) {\n customElements.define(\"cookbook-code-block\", CookbookCodeBlock);\n}\n"],"mappings":";;AAEA,IAAM,oBAAN,cAAgC,YAAY;CAC1C;CAEA,oBAAoB;EAClB,MAAM,SAAS,KAAK,cAAiC,kBAAkB;EACvE,IAAI,QAAQ;GACV,OAAO,aAAa,cAAc,QAAQ,UAAU,CAAC;GACrD,OAAO,QAAQ,QAAQ,UAAU;EACnC;EACA,KAAK,iBAAiB,SAAS,KAAK,KAAK;CAC3C;CAEA,uBAAuB;EACrB,KAAK,oBAAoB,SAAS,KAAK,KAAK;EAC5C,IAAI,KAAK,gBAAgB,KAAA,GACvB,OAAO,aAAa,KAAK,WAAW;CAExC;CAEA,QAAQ,OAAO,UAAsB;EACnC,MAAM,SAAS,MAAM;EACrB,MAAM,SACJ,kBAAkB,UACd,OAAO,QAA2B,kBAAkB,IACpD,KAAA;EACN,MAAM,OAAO,KAAK,cAA2B,UAAU;EACvD,IAAI,CAAC,UAAU,CAAC,KAAK,SAAS,MAAM,KAAK,CAAC,MAAM;EAEhD,IAAI;GACF,MAAM,eAAe,IAAI;EAC3B,QAAQ;GACN,OAAO,QAAQ,YAAY;GAC3B,OAAO,aAAa,cAAc,QAAQ,eAAe,CAAC;GAC1D,OAAO,QAAQ,QAAQ,eAAe;GACtC;EACF;EAEA,OAAO,QAAQ,YAAY;EAC3B,OAAO,aAAa,cAAc,QAAQ,YAAY,CAAC;EACvD,OAAO,QAAQ,QAAQ,YAAY;EAEnC,IAAI,KAAK,gBAAgB,KAAA,GACvB,OAAO,aAAa,KAAK,WAAW;EAEtC,KAAK,cAAc,OAAO,iBAAiB;GACzC,OAAO,OAAO,QAAQ;GACtB,OAAO,aAAa,cAAc,QAAQ,UAAU,CAAC;GACrD,OAAO,QAAQ,QAAQ,UAAU;GACjC,KAAK,cAAc,KAAA;EACrB,GAAG,GAAK;CACV;AACF;AAEA,eAAe,eAAe,MAAkC;CAC9D,MAAM,QAAQ,KAAK,eAAe;CAClC,IAAI,UAAU,WAAW,WAAW;EAClC,MAAM,UAAU,UAAU,UAAU,KAAK;EACzC;CACF;CAEA,MAAM,YAAY,OAAO,aAAa;CACtC,MAAM,iBAAiB,YACnB,MAAM,KAAK,EAAE,QAAQ,UAAU,WAAW,IAAI,GAAG,UAC/C,UAAU,WAAW,KAAK,CAC5B,IACA,CAAC;CACL,MAAM,QAAQ,SAAS,YAAY;CACnC,MAAM,mBAAmB,IAAI;CAC7B,WAAW,gBAAgB;CAC3B,WAAW,SAAS,KAAK;CACzB,MAAM,SAAS,SAAS,YAAY,MAAM;CAC1C,WAAW,gBAAgB;CAC3B,KAAK,MAAM,iBAAiB,gBAC1B,WAAW,SAAS,aAAa;CACnC,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,uBAAuB;AACtD;AAEA,IAAI,CAAC,eAAe,IAAI,qBAAqB,GAC3C,eAAe,OAAO,uBAAuB,iBAAiB"}
@@ -0,0 +1,6 @@
1
+ //#region src/client/heading-links.d.ts
2
+ /** Keep real fragment links as the no-JavaScript and modified-click fallback. */
3
+ declare function initializeHeadingLinks(): void;
4
+ //#endregion
5
+ export { initializeHeadingLinks };
6
+ //# sourceMappingURL=heading-links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heading-links.d.ts","names":[],"sources":["../../src/client/heading-links.ts"],"mappings":";;iBAEgB"}
@@ -0,0 +1,87 @@
1
+ import { t as message } from "../messages-B_3xUzUb.js";
2
+ //#region src/client/heading-links.ts
3
+ /** Keep real fragment links as the no-JavaScript and modified-click fallback. */
4
+ function initializeHeadingLinks() {
5
+ for (const content of document.querySelectorAll(".cookbook-markdown-content")) {
6
+ let status = content.querySelector("[data-heading-copy-status]");
7
+ if (!status) {
8
+ status = document.createElement("span");
9
+ status.className = "sr-only";
10
+ status.dataset.headingCopyStatus = "";
11
+ status.setAttribute("role", "status");
12
+ content.append(status);
13
+ }
14
+ for (const link of content.querySelectorAll(".cookbook-heading-wrapper > .cookbook-anchor-link[href^=\"#\"]")) {
15
+ if (link.dataset.copyReady) continue;
16
+ link.dataset.copyReady = "true";
17
+ const heading = link.previousElementSibling?.textContent?.trim() ?? "section";
18
+ const label = message("copyHeading").replace("{heading}", heading);
19
+ link.setAttribute("role", "button");
20
+ link.setAttribute("aria-label", label);
21
+ link.title = label;
22
+ let timer;
23
+ let attempt = 0;
24
+ link.addEventListener("keydown", (event) => {
25
+ if (event.key !== " " || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
26
+ event.preventDefault();
27
+ if (!event.repeat) link.click();
28
+ });
29
+ link.addEventListener("click", async (event) => {
30
+ if (event.defaultPrevented || event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
31
+ event.preventDefault();
32
+ window.clearTimeout(timer);
33
+ delete link.dataset.copyState;
34
+ status.textContent = "";
35
+ const currentAttempt = ++attempt;
36
+ let message$1;
37
+ let state;
38
+ try {
39
+ await copyLink(new URL(link.getAttribute("href"), window.location.href).href);
40
+ message$1 = message("linkCopied");
41
+ state = "copied";
42
+ } catch {
43
+ message$1 = message("linkCopyError");
44
+ state = "error";
45
+ }
46
+ if (currentAttempt !== attempt || !link.isConnected) return;
47
+ link.dataset.copyState = state;
48
+ link.setAttribute("aria-label", message$1);
49
+ link.title = message$1;
50
+ status.textContent = `${message$1}: ${heading}`;
51
+ timer = window.setTimeout(() => {
52
+ delete link.dataset.copyState;
53
+ link.setAttribute("aria-label", label);
54
+ link.title = label;
55
+ }, 2e3);
56
+ });
57
+ }
58
+ }
59
+ }
60
+ async function copyLink(value) {
61
+ if (navigator.clipboard?.writeText) {
62
+ await navigator.clipboard.writeText(value);
63
+ return;
64
+ }
65
+ const input = document.createElement("textarea");
66
+ input.className = "sr-only";
67
+ input.value = value;
68
+ input.readOnly = true;
69
+ const focused = document.activeElement;
70
+ const selection = window.getSelection();
71
+ const ranges = selection ? Array.from({ length: selection.rangeCount }, (_, index) => selection.getRangeAt(index)) : [];
72
+ document.body.append(input);
73
+ try {
74
+ input.focus({ preventScroll: true });
75
+ input.select();
76
+ if (!document.execCommand("copy")) throw new Error("Clipboard unavailable");
77
+ } finally {
78
+ input.remove();
79
+ if (focused instanceof HTMLElement) focused.focus({ preventScroll: true });
80
+ selection?.removeAllRanges();
81
+ for (const range of ranges) selection?.addRange(range);
82
+ }
83
+ }
84
+ //#endregion
85
+ export { initializeHeadingLinks };
86
+
87
+ //# sourceMappingURL=heading-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heading-links.js","names":["uiMessage","message"],"sources":["../../src/client/heading-links.ts"],"sourcesContent":["import { message as uiMessage } from \"./messages.js\";\n/** Keep real fragment links as the no-JavaScript and modified-click fallback. */\nexport function initializeHeadingLinks(): void {\n for (const content of document.querySelectorAll(\n \".cookbook-markdown-content\",\n )) {\n let status = content.querySelector<HTMLElement>(\n \"[data-heading-copy-status]\",\n );\n if (!status) {\n status = document.createElement(\"span\");\n status.className = \"sr-only\";\n status.dataset.headingCopyStatus = \"\";\n status.setAttribute(\"role\", \"status\");\n content.append(status);\n }\n\n for (const link of content.querySelectorAll<HTMLAnchorElement>(\n '.cookbook-heading-wrapper > .cookbook-anchor-link[href^=\"#\"]',\n )) {\n if (link.dataset.copyReady) continue;\n link.dataset.copyReady = \"true\";\n const heading =\n link.previousElementSibling?.textContent?.trim() ?? \"section\";\n const label = uiMessage(\"copyHeading\").replace(\"{heading}\", heading);\n link.setAttribute(\"role\", \"button\");\n link.setAttribute(\"aria-label\", label);\n link.title = label;\n let timer: number | undefined;\n let attempt = 0;\n\n link.addEventListener(\"keydown\", (event) => {\n if (\n event.key !== \" \" ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n event.shiftKey\n )\n return;\n event.preventDefault();\n if (!event.repeat) link.click();\n });\n link.addEventListener(\"click\", async (event) => {\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n event.shiftKey\n )\n return;\n event.preventDefault();\n window.clearTimeout(timer);\n delete link.dataset.copyState;\n status.textContent = \"\";\n const currentAttempt = ++attempt;\n let message: string;\n let state: string;\n try {\n const url = new URL(link.getAttribute(\"href\")!, window.location.href);\n await copyLink(url.href);\n message = uiMessage(\"linkCopied\");\n state = \"copied\";\n } catch {\n message = uiMessage(\"linkCopyError\");\n state = \"error\";\n }\n if (currentAttempt !== attempt || !link.isConnected) return;\n link.dataset.copyState = state;\n link.setAttribute(\"aria-label\", message);\n link.title = message;\n status.textContent = `${message}: ${heading}`;\n timer = window.setTimeout(() => {\n delete link.dataset.copyState;\n link.setAttribute(\"aria-label\", label);\n link.title = label;\n }, 2_000);\n });\n }\n }\n}\n\nasync function copyLink(value: string): Promise<void> {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(value);\n return;\n }\n\n // Older browsers and insecure local previews may not expose Clipboard API.\n const input = document.createElement(\"textarea\");\n input.className = \"sr-only\";\n input.value = value;\n input.readOnly = true;\n const focused = document.activeElement;\n const selection = window.getSelection();\n const ranges = selection\n ? Array.from({ length: selection.rangeCount }, (_, index) =>\n selection.getRangeAt(index),\n )\n : [];\n document.body.append(input);\n try {\n input.focus({ preventScroll: true });\n input.select();\n if (!document.execCommand(\"copy\")) throw new Error(\"Clipboard unavailable\");\n } finally {\n input.remove();\n if (focused instanceof HTMLElement) focused.focus({ preventScroll: true });\n selection?.removeAllRanges();\n for (const range of ranges) selection?.addRange(range);\n }\n}\n"],"mappings":";;;AAEA,SAAgB,yBAA+B;CAC7C,KAAK,MAAM,WAAW,SAAS,iBAC7B,4BACF,GAAG;EACD,IAAI,SAAS,QAAQ,cACnB,4BACF;EACA,IAAI,CAAC,QAAQ;GACX,SAAS,SAAS,cAAc,MAAM;GACtC,OAAO,YAAY;GACnB,OAAO,QAAQ,oBAAoB;GACnC,OAAO,aAAa,QAAQ,QAAQ;GACpC,QAAQ,OAAO,MAAM;EACvB;EAEA,KAAK,MAAM,QAAQ,QAAQ,iBACzB,gEACF,GAAG;GACD,IAAI,KAAK,QAAQ,WAAW;GAC5B,KAAK,QAAQ,YAAY;GACzB,MAAM,UACJ,KAAK,wBAAwB,aAAa,KAAK,KAAK;GACtD,MAAM,QAAQA,QAAU,aAAa,CAAC,CAAC,QAAQ,aAAa,OAAO;GACnE,KAAK,aAAa,QAAQ,QAAQ;GAClC,KAAK,aAAa,cAAc,KAAK;GACrC,KAAK,QAAQ;GACb,IAAI;GACJ,IAAI,UAAU;GAEd,KAAK,iBAAiB,YAAY,UAAU;IAC1C,IACE,MAAM,QAAQ,OACd,MAAM,UACN,MAAM,WACN,MAAM,WACN,MAAM,UAEN;IACF,MAAM,eAAe;IACrB,IAAI,CAAC,MAAM,QAAQ,KAAK,MAAM;GAChC,CAAC;GACD,KAAK,iBAAiB,SAAS,OAAO,UAAU;IAC9C,IACE,MAAM,oBACN,MAAM,WAAW,KACjB,MAAM,UACN,MAAM,WACN,MAAM,WACN,MAAM,UAEN;IACF,MAAM,eAAe;IACrB,OAAO,aAAa,KAAK;IACzB,OAAO,KAAK,QAAQ;IACpB,OAAO,cAAc;IACrB,MAAM,iBAAiB,EAAE;IACzB,IAAIC;IACJ,IAAI;IACJ,IAAI;KAEF,MAAM,SAAS,IADC,IAAI,KAAK,aAAa,MAAM,GAAI,OAAO,SAAS,IAC/C,CAAC,CAAC,IAAI;KACvB,YAAUD,QAAU,YAAY;KAChC,QAAQ;IACV,QAAQ;KACN,YAAUA,QAAU,eAAe;KACnC,QAAQ;IACV;IACA,IAAI,mBAAmB,WAAW,CAAC,KAAK,aAAa;IACrD,KAAK,QAAQ,YAAY;IACzB,KAAK,aAAa,cAAcC,SAAO;IACvC,KAAK,QAAQA;IACb,OAAO,cAAc,GAAGA,UAAQ,IAAI;IACpC,QAAQ,OAAO,iBAAiB;KAC9B,OAAO,KAAK,QAAQ;KACpB,KAAK,aAAa,cAAc,KAAK;KACrC,KAAK,QAAQ;IACf,GAAG,GAAK;GACV,CAAC;EACH;CACF;AACF;AAEA,eAAe,SAAS,OAA8B;CACpD,IAAI,UAAU,WAAW,WAAW;EAClC,MAAM,UAAU,UAAU,UAAU,KAAK;EACzC;CACF;CAGA,MAAM,QAAQ,SAAS,cAAc,UAAU;CAC/C,MAAM,YAAY;CAClB,MAAM,QAAQ;CACd,MAAM,WAAW;CACjB,MAAM,UAAU,SAAS;CACzB,MAAM,YAAY,OAAO,aAAa;CACtC,MAAM,SAAS,YACX,MAAM,KAAK,EAAE,QAAQ,UAAU,WAAW,IAAI,GAAG,UAC/C,UAAU,WAAW,KAAK,CAC5B,IACA,CAAC;CACL,SAAS,KAAK,OAAO,KAAK;CAC1B,IAAI;EACF,MAAM,MAAM,EAAE,eAAe,KAAK,CAAC;EACnC,MAAM,OAAO;EACb,IAAI,CAAC,SAAS,YAAY,MAAM,GAAG,MAAM,IAAI,MAAM,uBAAuB;CAC5E,UAAU;EACR,MAAM,OAAO;EACb,IAAI,mBAAmB,aAAa,QAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;EACzE,WAAW,gBAAgB;EAC3B,KAAK,MAAM,SAAS,QAAQ,WAAW,SAAS,KAAK;CACvD;AACF"}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,28 @@
1
+ //#region src/client/mobile-toc.ts
2
+ var CookbookMobileToc = class extends HTMLElement {
3
+ connectedCallback() {
4
+ this.addEventListener("click", this.navigate);
5
+ }
6
+ disconnectedCallback() {
7
+ this.removeEventListener("click", this.navigate);
8
+ }
9
+ navigate = (event) => {
10
+ if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
11
+ const link = event.target instanceof Element ? event.target.closest("a[href^=\"#\"]") : null;
12
+ if (!link || !this.contains(link)) return;
13
+ const target = document.getElementById(decodeURIComponent(link.hash.slice(1)));
14
+ if (!target) return;
15
+ const details = this.querySelector("details");
16
+ if (details) details.open = false;
17
+ if (!target.hasAttribute("tabindex")) {
18
+ target.tabIndex = -1;
19
+ target.addEventListener("blur", () => target.removeAttribute("tabindex"), { once: true });
20
+ }
21
+ target.focus({ preventScroll: true });
22
+ };
23
+ };
24
+ if (!customElements.get("cookbook-mobile-toc")) customElements.define("cookbook-mobile-toc", CookbookMobileToc);
25
+ //#endregion
26
+ export {};
27
+
28
+ //# sourceMappingURL=mobile-toc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile-toc.js","names":[],"sources":["../../src/client/mobile-toc.ts"],"sourcesContent":["class CookbookMobileToc extends HTMLElement {\n connectedCallback() {\n this.addEventListener(\"click\", this.navigate);\n }\n disconnectedCallback() {\n this.removeEventListener(\"click\", this.navigate);\n }\n navigate = (event: MouseEvent) => {\n if (\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n )\n return;\n const link =\n event.target instanceof Element\n ? event.target.closest<HTMLAnchorElement>('a[href^=\"#\"]')\n : null;\n if (!link || !this.contains(link)) return;\n const target = document.getElementById(\n decodeURIComponent(link.hash.slice(1)),\n );\n if (!target) return;\n const details = this.querySelector(\"details\");\n if (details) details.open = false;\n if (!target.hasAttribute(\"tabindex\")) {\n target.tabIndex = -1;\n target.addEventListener(\n \"blur\",\n () => target.removeAttribute(\"tabindex\"),\n { once: true },\n );\n }\n target.focus({ preventScroll: true });\n };\n}\nif (!customElements.get(\"cookbook-mobile-toc\"))\n customElements.define(\"cookbook-mobile-toc\", CookbookMobileToc);\n"],"mappings":";AAAA,IAAM,oBAAN,cAAgC,YAAY;CAC1C,oBAAoB;EAClB,KAAK,iBAAiB,SAAS,KAAK,QAAQ;CAC9C;CACA,uBAAuB;EACrB,KAAK,oBAAoB,SAAS,KAAK,QAAQ;CACjD;CACA,YAAY,UAAsB;EAChC,IACE,MAAM,WAAW,KACjB,MAAM,WACN,MAAM,WACN,MAAM,YACN,MAAM,QAEN;EACF,MAAM,OACJ,MAAM,kBAAkB,UACpB,MAAM,OAAO,QAA2B,gBAAc,IACtD;EACN,IAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,IAAI,GAAG;EACnC,MAAM,SAAS,SAAS,eACtB,mBAAmB,KAAK,KAAK,MAAM,CAAC,CAAC,CACvC;EACA,IAAI,CAAC,QAAQ;EACb,MAAM,UAAU,KAAK,cAAc,SAAS;EAC5C,IAAI,SAAS,QAAQ,OAAO;EAC5B,IAAI,CAAC,OAAO,aAAa,UAAU,GAAG;GACpC,OAAO,WAAW;GAClB,OAAO,iBACL,cACM,OAAO,gBAAgB,UAAU,GACvC,EAAE,MAAM,KAAK,CACf;EACF;EACA,OAAO,MAAM,EAAE,eAAe,KAAK,CAAC;CACtC;AACF;AACA,IAAI,CAAC,eAAe,IAAI,qBAAqB,GAC3C,eAAe,OAAO,uBAAuB,iBAAiB"}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,112 @@
1
+ //#region src/client/navigation.ts
2
+ var CookbookSidebarPane = class extends HTMLElement {
3
+ #listeners;
4
+ #inert = /* @__PURE__ */ new Map();
5
+ #trigger;
6
+ connectedCallback() {
7
+ this.#listeners?.abort();
8
+ this.#listeners = new AbortController();
9
+ const { signal } = this.#listeners;
10
+ this.#trigger = document.querySelector(".td-menu-button[popovertarget=\"cookbook__sidebar\"]") ?? void 0;
11
+ const desktop = matchMedia("(min-width: 50rem)");
12
+ const syncViewport = () => {
13
+ if (desktop.matches) {
14
+ if (this.matches(":popover-open")) this.hidePopover();
15
+ this.#release();
16
+ this.popover = null;
17
+ this.removeAttribute("role");
18
+ this.removeAttribute("aria-modal");
19
+ } else {
20
+ this.popover = "auto";
21
+ this.setAttribute("role", "dialog");
22
+ this.setAttribute("aria-modal", "true");
23
+ }
24
+ };
25
+ syncViewport();
26
+ desktop.addEventListener("change", syncViewport, { signal });
27
+ this.addEventListener("toggle", () => {
28
+ const open = !desktop.matches && this.matches(":popover-open");
29
+ this.#trigger?.setAttribute("aria-expanded", String(open));
30
+ if (open) {
31
+ this.getBoundingClientRect();
32
+ this.setAttribute("data-open", "");
33
+ for (const element of document.querySelectorAll(".header, .main-frame, .cookbook-skip-link")) {
34
+ this.#inert.set(element, element.inert);
35
+ element.inert = true;
36
+ }
37
+ this.querySelector("[autofocus]")?.focus({ preventScroll: true });
38
+ } else {
39
+ const restoreFocus = this.contains(document.activeElement) || document.activeElement === document.body;
40
+ this.#release();
41
+ if (restoreFocus && !desktop.matches) this.#trigger?.focus();
42
+ }
43
+ }, { signal });
44
+ this.addEventListener("keydown", (event) => {
45
+ if (desktop.matches || !this.matches(":popover-open")) return;
46
+ const sections = this.querySelector(".td-mobile-tabs details[open]");
47
+ if (event.key === "Escape" && sections) {
48
+ event.preventDefault();
49
+ sections.open = false;
50
+ sections.querySelector("summary")?.focus();
51
+ return;
52
+ }
53
+ if (event.key !== "Tab") return;
54
+ const focusable = Array.from(this.querySelectorAll("a[href], button:not([disabled]), select:not([disabled]), input:not([disabled]), summary, [tabindex=\"0\"]")).filter((element) => element.tabIndex >= 0 && element.getClientRects().length > 0);
55
+ const first = focusable[0];
56
+ const last = focusable.at(-1);
57
+ if (!first || !last) {
58
+ event.preventDefault();
59
+ this.focus();
60
+ return;
61
+ }
62
+ if (event.shiftKey && (document.activeElement === first || document.activeElement === this)) {
63
+ event.preventDefault();
64
+ last.focus();
65
+ } else if (!event.shiftKey && document.activeElement === last) {
66
+ event.preventDefault();
67
+ first.focus();
68
+ }
69
+ }, { signal });
70
+ this.addEventListener("click", (event) => {
71
+ if (!event.defaultPrevented && event.target instanceof Element && event.target.closest("a[href]") && this.matches(":popover-open")) this.hidePopover();
72
+ }, { signal });
73
+ }
74
+ #release() {
75
+ this.removeAttribute("data-open");
76
+ for (const [element, inert] of this.#inert) element.inert = inert;
77
+ this.#inert.clear();
78
+ this.#trigger?.setAttribute("aria-expanded", "false");
79
+ }
80
+ disconnectedCallback() {
81
+ this.#listeners?.abort();
82
+ this.#release();
83
+ }
84
+ };
85
+ var CookbookHeaderLinks = class extends HTMLElement {
86
+ #listeners;
87
+ connectedCallback() {
88
+ this.#listeners?.abort();
89
+ this.#listeners = new AbortController();
90
+ const { signal } = this.#listeners;
91
+ const panel = this.querySelector("[popover]");
92
+ const desktop = matchMedia("(min-width: 50rem)");
93
+ const close = () => {
94
+ if (panel?.matches(":popover-open")) panel.hidePopover();
95
+ };
96
+ desktop.addEventListener("change", () => {
97
+ if (desktop.matches) close();
98
+ }, { signal });
99
+ panel?.addEventListener("click", (event) => {
100
+ if (event.target instanceof Element && event.target.closest("a[href]")) close();
101
+ }, { signal });
102
+ }
103
+ disconnectedCallback() {
104
+ this.#listeners?.abort();
105
+ }
106
+ };
107
+ if (!customElements.get("cookbook-sidebar-pane")) customElements.define("cookbook-sidebar-pane", CookbookSidebarPane);
108
+ if (!customElements.get("cookbook-header-links")) customElements.define("cookbook-header-links", CookbookHeaderLinks);
109
+ //#endregion
110
+ export {};
111
+
112
+ //# sourceMappingURL=navigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.js","names":[],"sources":["../../src/client/navigation.ts"],"sourcesContent":["export {};\n\nclass CookbookSidebarPane extends HTMLElement {\n #listeners?: AbortController;\n #inert = new Map<HTMLElement, boolean>();\n #trigger: HTMLButtonElement | undefined;\n\n connectedCallback() {\n this.#listeners?.abort();\n this.#listeners = new AbortController();\n const { signal } = this.#listeners;\n this.#trigger =\n document.querySelector<HTMLButtonElement>(\n '.td-menu-button[popovertarget=\"cookbook__sidebar\"]',\n ) ?? undefined;\n const desktop = matchMedia(\"(min-width: 50rem)\");\n const syncViewport = () => {\n if (desktop.matches) {\n if (this.matches(\":popover-open\")) this.hidePopover();\n this.#release();\n this.popover = null;\n this.removeAttribute(\"role\");\n this.removeAttribute(\"aria-modal\");\n } else {\n this.popover = \"auto\";\n this.setAttribute(\"role\", \"dialog\");\n this.setAttribute(\"aria-modal\", \"true\");\n }\n };\n syncViewport();\n desktop.addEventListener(\"change\", syncViewport, { signal });\n this.addEventListener(\n \"toggle\",\n () => {\n const open = !desktop.matches && this.matches(\":popover-open\");\n this.#trigger?.setAttribute(\"aria-expanded\", String(open));\n if (open) {\n // Establish the closed position after the popover enters the top\n // layer so its attribute change transitions from an actual layout.\n // Tasty's global SSR renderer does not preserve @starting-style.\n this.getBoundingClientRect();\n this.setAttribute(\"data-open\", \"\");\n for (const element of document.querySelectorAll<HTMLElement>(\n \".header, .main-frame, .cookbook-skip-link\",\n )) {\n this.#inert.set(element, element.inert);\n element.inert = true;\n }\n this.querySelector<HTMLElement>(\"[autofocus]\")?.focus({\n preventScroll: true,\n });\n } else {\n const restoreFocus =\n this.contains(document.activeElement) ||\n document.activeElement === document.body;\n this.#release();\n if (restoreFocus && !desktop.matches) this.#trigger?.focus();\n }\n },\n { signal },\n );\n this.addEventListener(\n \"keydown\",\n (event) => {\n if (desktop.matches || !this.matches(\":popover-open\")) return;\n const sections = this.querySelector<HTMLDetailsElement>(\n \".td-mobile-tabs details[open]\",\n );\n if (event.key === \"Escape\" && sections) {\n event.preventDefault();\n sections.open = false;\n sections.querySelector(\"summary\")?.focus();\n return;\n }\n if (event.key !== \"Tab\") return;\n const focusable = Array.from(\n this.querySelectorAll<HTMLElement>(\n 'a[href], button:not([disabled]), select:not([disabled]), input:not([disabled]), summary, [tabindex=\"0\"]',\n ),\n ).filter(\n (element) =>\n element.tabIndex >= 0 && element.getClientRects().length > 0,\n );\n const first = focusable[0];\n const last = focusable.at(-1);\n if (!first || !last) {\n event.preventDefault();\n this.focus();\n return;\n }\n if (\n event.shiftKey &&\n (document.activeElement === first || document.activeElement === this)\n ) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first.focus();\n }\n },\n { signal },\n );\n this.addEventListener(\n \"click\",\n (event) => {\n if (\n !event.defaultPrevented &&\n event.target instanceof Element &&\n event.target.closest(\"a[href]\") &&\n this.matches(\":popover-open\")\n )\n this.hidePopover();\n },\n { signal },\n );\n }\n\n #release() {\n this.removeAttribute(\"data-open\");\n for (const [element, inert] of this.#inert) element.inert = inert;\n this.#inert.clear();\n this.#trigger?.setAttribute(\"aria-expanded\", \"false\");\n }\n\n disconnectedCallback() {\n this.#listeners?.abort();\n this.#release();\n }\n}\n\nclass CookbookHeaderLinks extends HTMLElement {\n #listeners?: AbortController;\n connectedCallback() {\n this.#listeners?.abort();\n this.#listeners = new AbortController();\n const { signal } = this.#listeners;\n const panel = this.querySelector<HTMLElement>(\"[popover]\");\n const desktop = matchMedia(\"(min-width: 50rem)\");\n const close = () => {\n if (panel?.matches(\":popover-open\")) panel.hidePopover();\n };\n desktop.addEventListener(\n \"change\",\n () => {\n if (desktop.matches) close();\n },\n { signal },\n );\n panel?.addEventListener(\n \"click\",\n (event) => {\n if (event.target instanceof Element && event.target.closest(\"a[href]\"))\n close();\n },\n { signal },\n );\n }\n disconnectedCallback() {\n this.#listeners?.abort();\n }\n}\n\nif (!customElements.get(\"cookbook-sidebar-pane\"))\n customElements.define(\"cookbook-sidebar-pane\", CookbookSidebarPane);\nif (!customElements.get(\"cookbook-header-links\"))\n customElements.define(\"cookbook-header-links\", CookbookHeaderLinks);\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,34 @@
1
+ import { t as message } from "../messages-B_3xUzUb.js";
2
+ //#region src/client/page-copy.ts
3
+ var CookbookPageActions = class extends HTMLElement {
4
+ connectedCallback() {
5
+ this.addEventListener("click", this.copy);
6
+ }
7
+ disconnectedCallback() {
8
+ this.removeEventListener("click", this.copy);
9
+ }
10
+ copy = async (event) => {
11
+ const button = event.target instanceof Element ? event.target.closest("[data-copy-page]") : null;
12
+ if (!button || !this.contains(button) || button.getAttribute("aria-disabled") === "true") return;
13
+ const status = this.querySelector("[role=\"status\"]");
14
+ button.setAttribute("aria-disabled", "true");
15
+ if (status) status.textContent = "";
16
+ try {
17
+ const response = await fetch(button.dataset.copyPage, { credentials: "same-origin" });
18
+ if (!response.ok) throw Error("Page text unavailable");
19
+ const text = await response.text();
20
+ if (!text.startsWith("---\n")) throw Error("Unexpected page response");
21
+ await navigator.clipboard.writeText(text);
22
+ if (status) status.textContent = message("pageCopied");
23
+ } catch {
24
+ if (status) status.textContent = message("pageCopyError");
25
+ } finally {
26
+ button.removeAttribute("aria-disabled");
27
+ }
28
+ };
29
+ };
30
+ if (!customElements.get("cookbook-page-actions")) customElements.define("cookbook-page-actions", CookbookPageActions);
31
+ //#endregion
32
+ export {};
33
+
34
+ //# sourceMappingURL=page-copy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-copy.js","names":[],"sources":["../../src/client/page-copy.ts"],"sourcesContent":["import { message } from \"./messages.js\";\nclass CookbookPageActions extends HTMLElement {\n connectedCallback() {\n this.addEventListener(\"click\", this.copy);\n }\n disconnectedCallback() {\n this.removeEventListener(\"click\", this.copy);\n }\n copy = async (event: Event) => {\n const button =\n event.target instanceof Element\n ? event.target.closest<HTMLButtonElement>(\"[data-copy-page]\")\n : null;\n if (\n !button ||\n !this.contains(button) ||\n button.getAttribute(\"aria-disabled\") === \"true\"\n )\n return;\n const status = this.querySelector<HTMLElement>('[role=\"status\"]');\n button.setAttribute(\"aria-disabled\", \"true\");\n if (status) status.textContent = \"\";\n try {\n const response = await fetch(button.dataset.copyPage!, {\n credentials: \"same-origin\",\n });\n if (!response.ok) throw Error(\"Page text unavailable\");\n const text = await response.text();\n if (!text.startsWith(\"---\\n\")) throw Error(\"Unexpected page response\");\n await navigator.clipboard.writeText(text);\n if (status) status.textContent = message(\"pageCopied\");\n } catch {\n if (status) status.textContent = message(\"pageCopyError\");\n } finally {\n button.removeAttribute(\"aria-disabled\");\n }\n };\n}\nif (!customElements.get(\"cookbook-page-actions\"))\n customElements.define(\"cookbook-page-actions\", CookbookPageActions);\n"],"mappings":";;AACA,IAAM,sBAAN,cAAkC,YAAY;CAC5C,oBAAoB;EAClB,KAAK,iBAAiB,SAAS,KAAK,IAAI;CAC1C;CACA,uBAAuB;EACrB,KAAK,oBAAoB,SAAS,KAAK,IAAI;CAC7C;CACA,OAAO,OAAO,UAAiB;EAC7B,MAAM,SACJ,MAAM,kBAAkB,UACpB,MAAM,OAAO,QAA2B,kBAAkB,IAC1D;EACN,IACE,CAAC,UACD,CAAC,KAAK,SAAS,MAAM,KACrB,OAAO,aAAa,eAAe,MAAM,QAEzC;EACF,MAAM,SAAS,KAAK,cAA2B,mBAAiB;EAChE,OAAO,aAAa,iBAAiB,MAAM;EAC3C,IAAI,QAAQ,OAAO,cAAc;EACjC,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,OAAO,QAAQ,UAAW,EACrD,aAAa,cACf,CAAC;GACD,IAAI,CAAC,SAAS,IAAI,MAAM,MAAM,uBAAuB;GACrD,MAAM,OAAO,MAAM,SAAS,KAAK;GACjC,IAAI,CAAC,KAAK,WAAW,OAAO,GAAG,MAAM,MAAM,0BAA0B;GACrE,MAAM,UAAU,UAAU,UAAU,IAAI;GACxC,IAAI,QAAQ,OAAO,cAAc,QAAQ,YAAY;EACvD,QAAQ;GACN,IAAI,QAAQ,OAAO,cAAc,QAAQ,eAAe;EAC1D,UAAU;GACR,OAAO,gBAAgB,eAAe;EACxC;CACF;AACF;AACA,IAAI,CAAC,eAAe,IAAI,uBAAuB,GAC7C,eAAe,OAAO,yBAAyB,mBAAmB"}
@@ -0,0 +1,13 @@
1
+ //#region src/client/search.d.ts
2
+ type SearchResult = {
3
+ url: string;
4
+ sub_results?: SearchResult[];
5
+ };
6
+ type PagefindOptions = Record<string, unknown> & {
7
+ processResult?: (result: SearchResult) => SearchResult | void;
8
+ };
9
+ /** Own the dialog lifecycle; Pagefind owns indexing, filtering and result rendering. */
10
+ declare function initializeSearch(element: HTMLElement, options: PagefindOptions, development: boolean, base: string): void;
11
+ //#endregion
12
+ export { initializeSearch };
13
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","names":[],"sources":["../../src/client/search.ts"],"mappings":";KAAK;EAAiB;EAAa,cAAc;;KAC5C,kBAAkB;EACrB,iBAAiB,QAAQ,iBAAiB;;;iBAI5B,iBACd,SAAS,aACT,SAAS,iBACT,sBACA"}