retypeset-odyssey 0.1.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 (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +168 -0
  3. package/astro.config.ts +18 -0
  4. package/default-config.yaml +136 -0
  5. package/discover-collections.ts +160 -0
  6. package/integration.ts +394 -0
  7. package/package.json +105 -0
  8. package/patches/@qwik.dev__partytown@0.11.2.patch +98 -0
  9. package/public/_redirects +819 -0
  10. package/public/feeds/atom-style.xsl +105 -0
  11. package/public/feeds/rss-style.xsl +105 -0
  12. package/public/fonts/EarlySummer-VF-Split/00785494587e3487ac63a0e7e4fa30f0.woff2 +0 -0
  13. package/public/fonts/EarlySummer-VF-Split/08e5d941a4c76fad7b68e7a937ebb21f.woff2 +0 -0
  14. package/public/fonts/EarlySummer-VF-Split/1268e5072156188d601f1eeb4473655d.woff2 +0 -0
  15. package/public/fonts/EarlySummer-VF-Split/12a385475353c815d7a5add53ee51e37.woff2 +0 -0
  16. package/public/fonts/EarlySummer-VF-Split/12b11ca08223c65a21fc731d59dcfc11.woff2 +0 -0
  17. package/public/fonts/EarlySummer-VF-Split/16d6676d3cb645c520ee6df8a1f89afd.woff2 +0 -0
  18. package/public/fonts/EarlySummer-VF-Split/2912a75ffef95e7a5ae9e2b2311ad61d.woff2 +0 -0
  19. package/public/fonts/EarlySummer-VF-Split/298d96ea561e419a4104bc9fc18499ce.woff2 +0 -0
  20. package/public/fonts/EarlySummer-VF-Split/2a2c71acc17ec39f6780835899e53096.woff2 +0 -0
  21. package/public/fonts/EarlySummer-VF-Split/2a7e2d0e59d3f638074c50fab39fdef1.woff2 +0 -0
  22. package/public/fonts/EarlySummer-VF-Split/36931fc4370e1670ed76af5d3feccba2.woff2 +0 -0
  23. package/public/fonts/EarlySummer-VF-Split/3a68fdc792e4a9e0399a04e32d0cc2e3.woff2 +0 -0
  24. package/public/fonts/EarlySummer-VF-Split/4054d6a4d6b37719b51e0f71da6e7cd9.woff2 +0 -0
  25. package/public/fonts/EarlySummer-VF-Split/429cb25f825c3cbde6bfac5b36ae9675.woff2 +0 -0
  26. package/public/fonts/EarlySummer-VF-Split/42a9efc11298368ecdc1b85ab46f0b4f.woff2 +0 -0
  27. package/public/fonts/EarlySummer-VF-Split/432018d2bdc9df92a7662056eb2b1261.woff2 +0 -0
  28. package/public/fonts/EarlySummer-VF-Split/44a6fb782f2a01560faa0f95248b60ef.woff2 +0 -0
  29. package/public/fonts/EarlySummer-VF-Split/45367b060e8ba0aa2507e6b91b86620b.woff2 +0 -0
  30. package/public/fonts/EarlySummer-VF-Split/571db7564bda7c1a93542881b8976f4b.woff2 +0 -0
  31. package/public/fonts/EarlySummer-VF-Split/58d55eeef4cf455e86a1142b1f3110d3.woff2 +0 -0
  32. package/public/fonts/EarlySummer-VF-Split/59ea41e77309160a0f63cdc76a010202.woff2 +0 -0
  33. package/public/fonts/EarlySummer-VF-Split/5d19d9174e568db4755981aa2e4ab380.woff2 +0 -0
  34. package/public/fonts/EarlySummer-VF-Split/5e811eb3b4175ee93d7ec000bf4631c2.woff2 +0 -0
  35. package/public/fonts/EarlySummer-VF-Split/6268e0cd5d66d6fe05b331f259e7b9e4.woff2 +0 -0
  36. package/public/fonts/EarlySummer-VF-Split/6549844aa3d833ca06a68a8e839db465.woff2 +0 -0
  37. package/public/fonts/EarlySummer-VF-Split/714b459658a7321ceeb1e1386ce165c2.woff2 +0 -0
  38. package/public/fonts/EarlySummer-VF-Split/7511d97a469915013683eae06cb21cd9.woff2 +0 -0
  39. package/public/fonts/EarlySummer-VF-Split/7784b4ebe543d13f62f6f6e05beb0b2e.woff2 +0 -0
  40. package/public/fonts/EarlySummer-VF-Split/77c9bea70b3c6ab24e1497d5468c825b.woff2 +0 -0
  41. package/public/fonts/EarlySummer-VF-Split/789ebea9e81df623e930b86de98fbfab.woff2 +0 -0
  42. package/public/fonts/EarlySummer-VF-Split/885bb7ab0717e8a47fc17f953adcdbf1.woff2 +0 -0
  43. package/public/fonts/EarlySummer-VF-Split/896c58aff69a9a857764cee0663bc56d.woff2 +0 -0
  44. package/public/fonts/EarlySummer-VF-Split/8fb6fc01c59d1e3ad1910b58dec7f5e7.woff2 +0 -0
  45. package/public/fonts/EarlySummer-VF-Split/95be5462b91b9a0458797cdc89d94cb5.woff2 +0 -0
  46. package/public/fonts/EarlySummer-VF-Split/9a5b2724f983ca0fc0d5ff8d10c41396.woff2 +0 -0
  47. package/public/fonts/EarlySummer-VF-Split/9ffe17f9c0e4cc4356cb3f08ffdb9c6d.woff2 +0 -0
  48. package/public/fonts/EarlySummer-VF-Split/EarlySummer-VF-Subset.woff2 +0 -0
  49. package/public/fonts/EarlySummer-VF-Split/EarlySummerSerif License.txt +91 -0
  50. package/public/fonts/EarlySummer-VF-Split/a097ef49be62cd2565aca45600e1e3ac.woff2 +0 -0
  51. package/public/fonts/EarlySummer-VF-Split/a17a1ae6063088e5b3a48c06b816929a.woff2 +0 -0
  52. package/public/fonts/EarlySummer-VF-Split/a83fdcfc5ecf2f6996704b0c02758689.woff2 +0 -0
  53. package/public/fonts/EarlySummer-VF-Split/a8cf15ff9b71e59407d8406866ff6f99.woff2 +0 -0
  54. package/public/fonts/EarlySummer-VF-Split/af530ed51dd519e4456f8a5e259e908b.woff2 +0 -0
  55. package/public/fonts/EarlySummer-VF-Split/b195a8924915deec4aa9c3ec777cc93f.woff2 +0 -0
  56. package/public/fonts/EarlySummer-VF-Split/b4b6bb5df9239dd67b52ca858fd2a506.woff2 +0 -0
  57. package/public/fonts/EarlySummer-VF-Split/b7592e1e027923f19e0e55dfdac69668.woff2 +0 -0
  58. package/public/fonts/EarlySummer-VF-Split/b965859f69d8ccceaf0e2d6292afbcfb.woff2 +0 -0
  59. package/public/fonts/EarlySummer-VF-Split/bbe9333f1ff242bd96ecb23ff9e723b1.woff2 +0 -0
  60. package/public/fonts/EarlySummer-VF-Split/be758580e295339ea98f0240b9869f24.woff2 +0 -0
  61. package/public/fonts/EarlySummer-VF-Split/c07099e1d025617f6d40966986e1941b.woff2 +0 -0
  62. package/public/fonts/EarlySummer-VF-Split/c1b593dda62fdeb7dde3af02016da282.woff2 +0 -0
  63. package/public/fonts/EarlySummer-VF-Split/c89f0335910a68a0958f2846108370e8.woff2 +0 -0
  64. package/public/fonts/EarlySummer-VF-Split/ca49aa409fdedd3f2f894cd20a16640a.woff2 +0 -0
  65. package/public/fonts/EarlySummer-VF-Split/ccd4a28d2f63797e0183c87792e20b75.woff2 +0 -0
  66. package/public/fonts/EarlySummer-VF-Split/d2718da923fce8e7ea229d65e306e92c.woff2 +0 -0
  67. package/public/fonts/EarlySummer-VF-Split/d893e9b307d96041e9cfcbd03761b9f4.woff2 +0 -0
  68. package/public/fonts/EarlySummer-VF-Split/dafaedaee41b75e21479d4ff324b6a34.woff2 +0 -0
  69. package/public/fonts/EarlySummer-VF-Split/db392af65f1867e5fd580eed2195df99.woff2 +0 -0
  70. package/public/fonts/EarlySummer-VF-Split/dc7c73a9e5577143ccd11e05ab55cb39.woff2 +0 -0
  71. package/public/fonts/EarlySummer-VF-Split/de396881189f747eba67685298363242.woff2 +0 -0
  72. package/public/fonts/EarlySummer-VF-Split/df625b213228bba22a7733d4eff8f148.woff2 +0 -0
  73. package/public/fonts/EarlySummer-VF-Split/e6e60b384f220b893ef31a926ece829a.woff2 +0 -0
  74. package/public/fonts/EarlySummer-VF-Split/e6e8ce2c5972ab665630bb705383d0fb.woff2 +0 -0
  75. package/public/fonts/EarlySummer-VF-Split/e963c7ed7104c2d6d68fcb5f952fe2f5.woff2 +0 -0
  76. package/public/fonts/EarlySummer-VF-Split/e966b23b4cd7783f43e31032d41784f4.woff2 +0 -0
  77. package/public/fonts/EarlySummer-VF-Split/edaac57c3856ec13128f4c6c3e00975c.woff2 +0 -0
  78. package/public/fonts/EarlySummer-VF-Split/ee54e0d86edf068c6c9cbddb76a856fe.woff2 +0 -0
  79. package/public/fonts/EarlySummer-VF-Split/f612c78a5544ff2dd3e8296ac3e58344.woff2 +0 -0
  80. package/public/fonts/EarlySummer-VF-Split/f9e539bd9b7bf999c3da82f5403ec3b6.woff2 +0 -0
  81. package/public/fonts/EarlySummer-VF-Split/fa5863b923ac15993c52a619f699ee63.woff2 +0 -0
  82. package/public/fonts/EarlySummer-VF-Split/fc759e56ec6f6e6d3d4cb163d62fb557.woff2 +0 -0
  83. package/public/fonts/Font Subset List/CJK Common Characters.txt +7534 -0
  84. package/public/fonts/Font Subset List/EarlySummer Subset.txt +3 -0
  85. package/public/fonts/Font Subset List/Japanese Kana + Korean Letters.txt +6123 -0
  86. package/public/fonts/Font Subset List/Latin + Cyrillic + Greek + Arabic Glyphs.txt +121 -0
  87. package/public/fonts/Font Subset List/unicode_range.py +49 -0
  88. package/public/fonts/NotoSansSC-Bold.otf +0 -0
  89. package/public/fonts/NotoSansSC-Regular.otf +0 -0
  90. package/public/fonts/STIX-Italic-VF.woff2 +0 -0
  91. package/public/fonts/STIX-VF.woff2 +0 -0
  92. package/public/fonts/Snell-Black-SF.woff2 +0 -0
  93. package/public/fonts/Snell-Bold-SF.woff2 +0 -0
  94. package/public/giscus/theme-dark.css +208 -0
  95. package/public/giscus/theme-light.css +208 -0
  96. package/public/icons/favicon.svg +4 -0
  97. package/public/icons/og-logo.png +0 -0
  98. package/public/robots.txt +4 -0
  99. package/public/sounds/tap_01.wav +0 -0
  100. package/public/sounds/tap_02.wav +0 -0
  101. package/public/sounds/tap_03.wav +0 -0
  102. package/public/sounds/tap_04.wav +0 -0
  103. package/public/sounds/tap_05.wav +0 -0
  104. package/public/sounds/type_01.wav +0 -0
  105. package/public/sounds/type_02.wav +0 -0
  106. package/public/sounds/type_03.wav +0 -0
  107. package/public/sounds/type_04.wav +0 -0
  108. package/public/sounds/type_05.wav +0 -0
  109. package/scripts/apply-lqip.ts +276 -0
  110. package/scripts/format-posts.ts +105 -0
  111. package/scripts/migration/README.md +52 -0
  112. package/scripts/migration/migrate-hexo.ts +185 -0
  113. package/scripts/migration/validate-abbrlinks.ts +161 -0
  114. package/scripts/new-post.ts +52 -0
  115. package/scripts/seo/generate-legacy-redirects.ts +407 -0
  116. package/scripts/update-theme.ts +46 -0
  117. package/src/assets/icons/copy-check.svg +3 -0
  118. package/src/assets/icons/copy-icon.svg +4 -0
  119. package/src/assets/icons/go-back.svg +3 -0
  120. package/src/assets/icons/heading-anchor.svg +4 -0
  121. package/src/assets/icons/lang-en.svg +3 -0
  122. package/src/assets/icons/lang-ja.svg +5 -0
  123. package/src/assets/icons/lang-zh.svg +5 -0
  124. package/src/assets/icons/language-switcher.svg +3 -0
  125. package/src/assets/icons/pin-icon.svg +3 -0
  126. package/src/assets/icons/search-icon.svg +3 -0
  127. package/src/assets/icons/theme-toggle.svg +3 -0
  128. package/src/assets/icons/toc-icon.svg +3 -0
  129. package/src/assets/icons/top-icon.svg +3 -0
  130. package/src/assets/lqip-map.json +10 -0
  131. package/src/components/Button.astro +152 -0
  132. package/src/components/CategoryList.astro +66 -0
  133. package/src/components/Comment/Giscus.astro +119 -0
  134. package/src/components/Comment/Index.astro +30 -0
  135. package/src/components/Comment/Twikoo.astro +114 -0
  136. package/src/components/Comment/Waline.astro +149 -0
  137. package/src/components/FloatingButtons.astro +101 -0
  138. package/src/components/Footer.astro +74 -0
  139. package/src/components/Header.astro +62 -0
  140. package/src/components/JournalList.astro +56 -0
  141. package/src/components/Navbar.astro +69 -0
  142. package/src/components/NoteList.astro +56 -0
  143. package/src/components/Pagination.astro +267 -0
  144. package/src/components/PostDate.astro +80 -0
  145. package/src/components/PostList.astro +87 -0
  146. package/src/components/SearchModal.astro +340 -0
  147. package/src/components/TagList.astro +135 -0
  148. package/src/components/Widgets/BackButton.astro +43 -0
  149. package/src/components/Widgets/CodeCopyButton.astro +47 -0
  150. package/src/components/Widgets/GithubCard.astro +110 -0
  151. package/src/components/Widgets/ImageZoom.astro +135 -0
  152. package/src/components/Widgets/MediaEmbed.astro +127 -0
  153. package/src/components/Widgets/SoundEffect.astro +179 -0
  154. package/src/components/Widgets/TOC.astro +198 -0
  155. package/src/config-schema.ts +164 -0
  156. package/src/config.ts +127 -0
  157. package/src/config.ts.example +205 -0
  158. package/src/content/about/_example-about-en.md +6 -0
  159. package/src/content/about/about-en.md +21 -0
  160. package/src/content/about/about-ja.md +21 -0
  161. package/src/content/about/about-zh.md +24 -0
  162. package/src/content.config.ts +247 -0
  163. package/src/env.d.ts +25 -0
  164. package/src/i18n/config.ts +65 -0
  165. package/src/i18n/lang.ts +70 -0
  166. package/src/i18n/path.ts +160 -0
  167. package/src/i18n/ui.ts +214 -0
  168. package/src/layouts/Head.astro +203 -0
  169. package/src/layouts/Layout.astro +69 -0
  170. package/src/pages/404.astro +20 -0
  171. package/src/pages/[...lang]/[...page].astro +48 -0
  172. package/src/pages/[...lang]/about.astro +28 -0
  173. package/src/pages/[...lang]/atom.xml.ts +14 -0
  174. package/src/pages/[...lang]/categories/index.astro +35 -0
  175. package/src/pages/[...lang]/journals/[slug].astro +89 -0
  176. package/src/pages/[...lang]/journals/index.astro +55 -0
  177. package/src/pages/[...lang]/journals/page/[page].astro +66 -0
  178. package/src/pages/[...lang]/notes/[slug].astro +88 -0
  179. package/src/pages/[...lang]/notes/index.astro +55 -0
  180. package/src/pages/[...lang]/notes/page/[page].astro +66 -0
  181. package/src/pages/[...lang]/posts/[slug].astro +101 -0
  182. package/src/pages/[...lang]/rss.xml.ts +14 -0
  183. package/src/pages/[...lang]/search.astro +65 -0
  184. package/src/pages/[...lang]/tags/[tag].astro +53 -0
  185. package/src/pages/[...lang]/tags/index.astro +36 -0
  186. package/src/pages/_dynamic/list.astro +101 -0
  187. package/src/pages/_dynamic/slug.astro +100 -0
  188. package/src/pages/og/[...image].ts +114 -0
  189. package/src/pages/robots.txt.ts +20 -0
  190. package/src/plugins/rehype-code-copy-button.mjs +82 -0
  191. package/src/plugins/rehype-external-links.mjs +18 -0
  192. package/src/plugins/rehype-heading-anchor.mjs +55 -0
  193. package/src/plugins/rehype-image-processor.mjs +77 -0
  194. package/src/plugins/remark-container-directives.mjs +135 -0
  195. package/src/plugins/remark-leaf-directives.mjs +184 -0
  196. package/src/plugins/remark-reading-time.mjs +11 -0
  197. package/src/styles/comment.css +205 -0
  198. package/src/styles/extension.css +180 -0
  199. package/src/styles/font.css +111 -0
  200. package/src/styles/global.css +91 -0
  201. package/src/styles/lqip.css +71 -0
  202. package/src/styles/markdown.css +276 -0
  203. package/src/styles/transition.css +173 -0
  204. package/src/types/global.d.ts +22 -0
  205. package/src/types/index.d.ts +111 -0
  206. package/src/utils/cache.ts +32 -0
  207. package/src/utils/content.ts +819 -0
  208. package/src/utils/description.ts +147 -0
  209. package/src/utils/dynamic-collections.ts +155 -0
  210. package/src/utils/feed.ts +238 -0
  211. package/src/utils/page.ts +107 -0
  212. package/tsconfig.json +13 -0
  213. package/uno.config.ts +75 -0
package/integration.ts ADDED
@@ -0,0 +1,394 @@
1
+ import type { AstroIntegration } from 'astro'
2
+ import mdx from '@astrojs/mdx'
3
+ import partytown from '@astrojs/partytown'
4
+ import sitemap from '@astrojs/sitemap'
5
+ import Compress from 'astro-compress'
6
+ import pagefind from 'astro-pagefind'
7
+ import { existsSync, readFileSync } from 'node:fs'
8
+ import { isAbsolute, resolve as resolvePath } from 'node:path'
9
+ import { fileURLToPath } from 'node:url'
10
+ import rehypeKatex from 'rehype-katex'
11
+ import rehypeMermaid from 'rehype-mermaid'
12
+ import rehypeSlug from 'rehype-slug'
13
+ import remarkDirective from 'remark-directive'
14
+ import remarkMath from 'remark-math'
15
+ import UnoCSS from 'unocss/astro'
16
+ import yaml from 'yaml'
17
+ import { discoverCollections } from './discover-collections'
18
+ import { ThemeConfigSchema } from './src/config-schema'
19
+ import { langMap } from './src/i18n/config'
20
+ import { rehypeCodeCopyButton } from './src/plugins/rehype-code-copy-button.mjs'
21
+ import { rehypeExternalLinks } from './src/plugins/rehype-external-links.mjs'
22
+ import { rehypeHeadingAnchor } from './src/plugins/rehype-heading-anchor.mjs'
23
+ import { rehypeImageProcessor } from './src/plugins/rehype-image-processor.mjs'
24
+ import { remarkContainerDirectives } from './src/plugins/remark-container-directives.mjs'
25
+ import { remarkLeafDirectives } from './src/plugins/remark-leaf-directives.mjs'
26
+ import { remarkReadingTime } from './src/plugins/remark-reading-time.mjs'
27
+
28
+ interface RetypesetOptions {
29
+ /**
30
+ * Path to a user `retypeset.config.yaml`.
31
+ *
32
+ * - Absolute path: used as-is.
33
+ * - Relative path: resolved against the consumer project root.
34
+ * - Omitted: looks for `retypeset.config.yaml` at the project root.
35
+ */
36
+ config?: string
37
+ }
38
+
39
+ /**
40
+ * Minimal deep-merge for plain objects + arrays.
41
+ *
42
+ * Arrays are replaced wholesale (so a consumer overriding `footer.links`
43
+ * gets exactly what they wrote, not the defaults concatenated with their
44
+ * additions). Objects are merged key by key. Anything else is replaced.
45
+ */
46
+ function deepMerge<T>(base: T, override: unknown): T {
47
+ if (override === undefined || override === null)
48
+ return base
49
+ if (
50
+ typeof base !== 'object'
51
+ || base === null
52
+ || Array.isArray(base)
53
+ || typeof override !== 'object'
54
+ || Array.isArray(override)
55
+ ) {
56
+ return override as T
57
+ }
58
+ const out: Record<string, unknown> = { ...(base as Record<string, unknown>) }
59
+ for (const [key, value] of Object.entries(override as Record<string, unknown>)) {
60
+ out[key] = deepMerge((base as Record<string, unknown>)[key], value)
61
+ }
62
+ return out as T
63
+ }
64
+
65
+ /**
66
+ * Retypeset Odyssey theme integration.
67
+ *
68
+ * Responsibilities:
69
+ *
70
+ * 1. Read `default-config.yaml` (shipped with the package) and optionally a
71
+ * consumer `retypeset.config.yaml` from the project root; deep-merge and
72
+ * Zod-validate them.
73
+ * 2. Expose the merged config via a Vite virtual module
74
+ * (`virtual:retypeset/config`) and re-route `@/config` imports to it, so
75
+ * every theme file that does `import ... from '@/config'` automatically
76
+ * sees consumer overrides without any code change.
77
+ * 3. Drive the Astro top-level config from the same YAML — `site`,
78
+ * `build.format`, `trailingSlash`, `prefetch`, `i18n`, and `image.domains`
79
+ * are all set from the validated config so the consumer's
80
+ * `astro.config.ts` can shrink to `integrations: [retypeset()]`.
81
+ * 4. Register UnoCSS, MDX, partytown, sitemap, pagefind, compress, and the
82
+ * markdown plugin pipeline, so consumers do not import any of those.
83
+ * 5. Inject all theme pages/routes.
84
+ *
85
+ * Usage in consumer's astro.config.ts:
86
+ * import retypeset from 'retypeset-odyssey/integration'
87
+ * export default defineConfig({ integrations: [retypeset()] })
88
+ */
89
+ export default function retypesetTheme(options: RetypesetOptions = {}): AstroIntegration {
90
+ return {
91
+ name: 'retypeset-odyssey',
92
+ hooks: {
93
+ 'astro:config:setup': ({ injectRoute, updateConfig, config }) => {
94
+ // Resolve paths relative to this package.
95
+ const themeUrl = (path: string) => new URL(path, import.meta.url)
96
+ const themePath = (path: string) => fileURLToPath(themeUrl(path))
97
+
98
+ // --- 1. Load + validate configuration ---
99
+
100
+ const defaultYamlPath = themePath('./default-config.yaml')
101
+ const defaultConfig = yaml.parse(readFileSync(defaultYamlPath, 'utf-8'))
102
+
103
+ const projectRoot = fileURLToPath(config.root)
104
+ let userConfigPath: string | null = null
105
+ if (options.config) {
106
+ userConfigPath = isAbsolute(options.config)
107
+ ? options.config
108
+ : resolvePath(projectRoot, options.config)
109
+ }
110
+ else {
111
+ const candidate = resolvePath(projectRoot, 'retypeset.config.yaml')
112
+ if (existsSync(candidate))
113
+ userConfigPath = candidate
114
+ }
115
+
116
+ let userConfig: unknown = {}
117
+ if (userConfigPath && existsSync(userConfigPath)) {
118
+ userConfig = yaml.parse(readFileSync(userConfigPath, 'utf-8')) ?? {}
119
+ }
120
+
121
+ const merged = deepMerge(defaultConfig, userConfig)
122
+ const validated = ThemeConfigSchema.parse(merged)
123
+
124
+ const base = validated.site.base === '/' ? '' : validated.site.base.replace(/\/$/, '')
125
+ const defaultLocale = validated.global.locale
126
+ const moreLocales = validated.global.moreLocales
127
+ const allLocales = [defaultLocale, ...moreLocales]
128
+
129
+ // --- 2. Build the virtual module body ---
130
+ //
131
+ // Mirrors the public surface of src/config.ts exactly so any file
132
+ // importing from '@/config' keeps working.
133
+ const configJson = JSON.stringify(validated)
134
+ const allLocalesJson = JSON.stringify(allLocales)
135
+ const moreLocalesJson = JSON.stringify(moreLocales)
136
+ const virtualConfigCode = `// Auto-generated by retypeset-odyssey integration. Do not edit.
137
+ export const themeConfig = ${configJson}
138
+ export const base = ${JSON.stringify(base)}
139
+ export const defaultLocale = ${JSON.stringify(defaultLocale)}
140
+ export const moreLocales = ${moreLocalesJson}
141
+ export const allLocales = ${allLocalesJson}
142
+ export const POSTS_PER_PAGE = 7
143
+ export const NOTES_PER_PAGE = 7
144
+ export const JOURNALS_PER_PAGE = 7
145
+ `
146
+
147
+ const VIRTUAL_ID = 'virtual:retypeset/config'
148
+ const RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_ID}`
149
+
150
+ // --- Collection discovery ---
151
+ //
152
+ // Decide which built-in collection routes to inject and which dynamic
153
+ // folders need their own list+detail routes. Built-ins default to
154
+ // enabled; consumers turn them off with
155
+ // `collections.<name>.enabled: false` in retypeset.config.yaml.
156
+ const { enabledBuiltIns, dynamicFolders, skippedFolders } = discoverCollections(
157
+ projectRoot,
158
+ userConfigPath ?? undefined,
159
+ )
160
+ const builtInEnabled = (name: 'posts' | 'notes' | 'journals') =>
161
+ enabledBuiltIns.includes(name)
162
+
163
+ if (skippedFolders.length > 0) {
164
+ // eslint-disable-next-line no-console
165
+ console.warn(
166
+ `[retypeset-odyssey] Skipped content folders with unsupported names: ${skippedFolders.join(', ')}. `
167
+ + `Folder names must match /^[a-zA-Z0-9][a-zA-Z0-9-]*$/ and must not be \`about\`.`,
168
+ )
169
+ }
170
+
171
+ // Expose the dynamic-collection list to template files via a virtual
172
+ // module so `getStaticPaths` knows which folders to enumerate.
173
+ const DYNAMIC_VIRTUAL_ID = 'virtual:retypeset/dynamic-collections'
174
+ const RESOLVED_DYNAMIC_VIRTUAL_ID = `\0${DYNAMIC_VIRTUAL_ID}`
175
+ const dynamicCollectionsCode = `// Auto-generated by retypeset-odyssey integration. Do not edit.
176
+ export const dynamicCollections = ${JSON.stringify(dynamicFolders)}
177
+ `
178
+
179
+ // --- 3. Inject all theme pages ---
180
+
181
+ // 404
182
+ injectRoute({ pattern: '/404', entrypoint: themeUrl('./src/pages/404.astro'), prerender: true })
183
+
184
+ // Homepage / paginated index
185
+ injectRoute({ pattern: '/[...lang]/[...page]', entrypoint: themeUrl('./src/pages/[...lang]/[...page].astro'), prerender: true })
186
+
187
+ // About
188
+ injectRoute({ pattern: '/[...lang]/about', entrypoint: themeUrl('./src/pages/[...lang]/about.astro'), prerender: true })
189
+
190
+ // Feeds
191
+ injectRoute({ pattern: '/[...lang]/atom.xml', entrypoint: themeUrl('./src/pages/[...lang]/atom.xml.ts'), prerender: true })
192
+ injectRoute({ pattern: '/[...lang]/rss.xml', entrypoint: themeUrl('./src/pages/[...lang]/rss.xml.ts'), prerender: true })
193
+
194
+ // Categories
195
+ injectRoute({ pattern: '/[...lang]/categories', entrypoint: themeUrl('./src/pages/[...lang]/categories/index.astro'), prerender: true })
196
+
197
+ // Posts (always injected; disabling `posts` only suppresses the navbar
198
+ // entry — the homepage and tag pages still depend on the collection).
199
+ if (builtInEnabled('posts')) {
200
+ injectRoute({ pattern: '/[...lang]/posts/[slug]', entrypoint: themeUrl('./src/pages/[...lang]/posts/[slug].astro'), prerender: true })
201
+ }
202
+
203
+ // Notes
204
+ if (builtInEnabled('notes')) {
205
+ injectRoute({ pattern: '/[...lang]/notes/[slug]', entrypoint: themeUrl('./src/pages/[...lang]/notes/[slug].astro'), prerender: true })
206
+ injectRoute({ pattern: '/[...lang]/notes', entrypoint: themeUrl('./src/pages/[...lang]/notes/index.astro'), prerender: true })
207
+ injectRoute({ pattern: '/[...lang]/notes/page/[page]', entrypoint: themeUrl('./src/pages/[...lang]/notes/page/[page].astro'), prerender: true })
208
+ }
209
+
210
+ // Journals
211
+ if (builtInEnabled('journals')) {
212
+ injectRoute({ pattern: '/[...lang]/journals/[slug]', entrypoint: themeUrl('./src/pages/[...lang]/journals/[slug].astro'), prerender: true })
213
+ injectRoute({ pattern: '/[...lang]/journals', entrypoint: themeUrl('./src/pages/[...lang]/journals/index.astro'), prerender: true })
214
+ injectRoute({ pattern: '/[...lang]/journals/page/[page]', entrypoint: themeUrl('./src/pages/[...lang]/journals/page/[page].astro'), prerender: true })
215
+ }
216
+
217
+ // Search
218
+ injectRoute({ pattern: '/[...lang]/search', entrypoint: themeUrl('./src/pages/[...lang]/search.astro'), prerender: true })
219
+
220
+ // Tags
221
+ injectRoute({ pattern: '/[...lang]/tags', entrypoint: themeUrl('./src/pages/[...lang]/tags/index.astro'), prerender: true })
222
+ injectRoute({ pattern: '/[...lang]/tags/[tag]', entrypoint: themeUrl('./src/pages/[...lang]/tags/[tag].astro'), prerender: true })
223
+
224
+ // OG images
225
+ injectRoute({ pattern: '/og/[...image]', entrypoint: themeUrl('./src/pages/og/[...image].ts'), prerender: true })
226
+
227
+ // robots.txt
228
+ injectRoute({ pattern: '/robots.txt', entrypoint: themeUrl('./src/pages/robots.txt.ts'), prerender: true })
229
+
230
+ // Dynamic folder routes. A single list pattern + single detail
231
+ // pattern cover all discovered folders; `getStaticPaths` in the
232
+ // template files only emits URLs for known folders, so Astro will
233
+ // not try to serve `/foo` unless `content/foo/` exists.
234
+ //
235
+ // Literal-segment routes (`/posts`, `/notes`, `/about`, `/tags`,
236
+ // etc.) take priority over single dynamic segments in Astro's
237
+ // router, so the built-ins are unaffected.
238
+ if (dynamicFolders.length > 0) {
239
+ injectRoute({
240
+ pattern: '/[...lang]/[collection]',
241
+ entrypoint: themeUrl('./src/pages/_dynamic/list.astro'),
242
+ prerender: true,
243
+ })
244
+ injectRoute({
245
+ pattern: '/[...lang]/[collection]/[slug]',
246
+ entrypoint: themeUrl('./src/pages/_dynamic/slug.astro'),
247
+ prerender: true,
248
+ })
249
+ }
250
+
251
+ // --- 4. Compute optional image config ---
252
+ // Only set `image.domains` when the user provides a host.
253
+ const { imageHostURL } = validated.preload ?? {}
254
+ const imageConfig = imageHostURL
255
+ ? {
256
+ image: {
257
+ domains: [imageHostURL],
258
+ remotePatterns: [{ protocol: 'https' as const }],
259
+ },
260
+ }
261
+ : {}
262
+
263
+ // --- 5. Push everything onto the Astro config ---
264
+ updateConfig({
265
+ site: validated.site.url,
266
+ base,
267
+ build: {
268
+ format: 'file', // Generates /posts/xxx.html instead of /posts/xxx/index.html
269
+ },
270
+ trailingSlash: 'never', // Required for build.format: 'file'
271
+ prefetch: {
272
+ prefetchAll: true,
273
+ defaultStrategy: 'viewport',
274
+ },
275
+ ...imageConfig,
276
+ i18n: {
277
+ locales: allLocales.map(lang => ({
278
+ path: lang,
279
+ codes: [...langMap[lang]] as [string, ...string[]],
280
+ })),
281
+ defaultLocale,
282
+ // Provide an explicit routing object so mergeConfig does not leave
283
+ // it undefined (Astro's downstream code reads `routing.redirectToDefaultLocale`).
284
+ routing: {
285
+ prefixDefaultLocale: false,
286
+ redirectToDefaultLocale: false,
287
+ fallbackType: 'redirect',
288
+ },
289
+ },
290
+ integrations: [
291
+ UnoCSS({
292
+ configFile: themePath('./uno.config.ts'),
293
+ injectReset: true,
294
+ }),
295
+ mdx(),
296
+ partytown({
297
+ config: {
298
+ forward: ['dataLayer.push', 'gtag'],
299
+ },
300
+ }),
301
+ sitemap(),
302
+ pagefind(),
303
+ Compress({
304
+ CSS: true,
305
+ HTML: true,
306
+ Image: false,
307
+ JavaScript: true,
308
+ SVG: false,
309
+ }),
310
+ ],
311
+ markdown: {
312
+ remarkPlugins: [
313
+ remarkDirective,
314
+ remarkMath,
315
+ remarkContainerDirectives,
316
+ remarkLeafDirectives,
317
+ remarkReadingTime,
318
+ ],
319
+ rehypePlugins: [
320
+ rehypeKatex,
321
+ [rehypeMermaid, { strategy: 'pre-mermaid' }],
322
+ rehypeSlug,
323
+ rehypeHeadingAnchor,
324
+ rehypeImageProcessor,
325
+ rehypeExternalLinks,
326
+ rehypeCodeCopyButton,
327
+ ],
328
+ syntaxHighlight: {
329
+ type: 'shiki',
330
+ excludeLangs: ['mermaid'],
331
+ },
332
+ shikiConfig: {
333
+ themes: {
334
+ light: 'github-light',
335
+ dark: 'github-dark',
336
+ },
337
+ },
338
+ },
339
+ // Astro internally calls fileURLToPath() on publicDir, so it must
340
+ // be a URL object.
341
+ publicDir: themeUrl('./public/') as any,
342
+ vite: {
343
+ plugins: [
344
+ {
345
+ // Vite 7 (Astro 6) is stricter about resolve.alias replacements:
346
+ // pointing an alias at a virtual module specifier (which then
347
+ // gets resolved by a separate plugin) is no longer reliable
348
+ // across all build environments (the content-collections /
349
+ // prerender bundle does not pick it up). Intercept the
350
+ // `@/config` import directly in `resolveId` so the virtual
351
+ // module always wins, regardless of alias evaluation order.
352
+ name: 'retypeset-config-loader',
353
+ enforce: 'pre',
354
+ resolveId(id) {
355
+ if (id === '@/config' || id === '@/config.ts' || id === VIRTUAL_ID)
356
+ return RESOLVED_VIRTUAL_ID
357
+ if (id === DYNAMIC_VIRTUAL_ID)
358
+ return RESOLVED_DYNAMIC_VIRTUAL_ID
359
+ return null
360
+ },
361
+ load(id) {
362
+ if (id === RESOLVED_VIRTUAL_ID)
363
+ return virtualConfigCode
364
+ if (id === RESOLVED_DYNAMIC_VIRTUAL_ID)
365
+ return dynamicCollectionsCode
366
+ return null
367
+ },
368
+ },
369
+ {
370
+ name: 'retypeset-prefix-font-urls-with-base',
371
+ transform(code, id) {
372
+ if (!id.endsWith('src/styles/font.css'))
373
+ return null
374
+ return code.replace(/url\("\/fonts\//g, `url("${base}/fonts/`)
375
+ },
376
+ },
377
+ ],
378
+ resolve: {
379
+ // Generic `@/*` alias for all other source paths. `@/config` is
380
+ // handled by the plugin above (which runs in the `pre` phase,
381
+ // before alias resolution).
382
+ alias: [
383
+ {
384
+ find: /^@\/(.*)/,
385
+ replacement: `${themePath('./src/')}$1`,
386
+ },
387
+ ],
388
+ },
389
+ },
390
+ })
391
+ },
392
+ },
393
+ }
394
+ }
package/package.json ADDED
@@ -0,0 +1,105 @@
1
+ {
2
+ "name": "retypeset-odyssey",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "packageManager": "pnpm@10.26.0",
6
+ "repository": "https://github.com/lifeodyssey/retypeset-odyssey",
7
+ "exports": {
8
+ ".": "./integration.ts",
9
+ "./integration": "./integration.ts",
10
+ "./config": "./src/config.ts",
11
+ "./content-config": "./src/content.config.ts",
12
+ "./astro.config": "./astro.config.ts",
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "public",
18
+ "patches",
19
+ "scripts",
20
+ "integration.ts",
21
+ "discover-collections.ts",
22
+ "astro.config.ts",
23
+ "tsconfig.json",
24
+ "uno.config.ts",
25
+ "default-config.yaml"
26
+ ],
27
+ "scripts": {
28
+ "dev": "astro check && astro dev",
29
+ "build": "pnpm generate-legacy-redirects && astro check && astro build && pnpm apply-lqip",
30
+ "preview": "astro preview",
31
+ "test": "playwright test",
32
+ "test:ui": "playwright test --ui",
33
+ "astro": "astro",
34
+ "lint": "eslint .",
35
+ "lint:fix": "eslint . --fix",
36
+ "new-post": "tsx scripts/new-post.ts",
37
+ "translate:new-posts": "node scripts/translation/run.mjs",
38
+ "generate-legacy-redirects": "tsx scripts/seo/generate-legacy-redirects.ts",
39
+ "apply-lqip": "tsx scripts/apply-lqip.ts",
40
+ "format-posts": "tsx scripts/format-posts.ts",
41
+ "update-theme": "tsx scripts/update-theme.ts"
42
+ },
43
+ "dependencies": {
44
+ "@astrojs/mdx": "^5.0.6",
45
+ "@astrojs/partytown": "^2.1.7",
46
+ "@astrojs/sitemap": "^3.7.2",
47
+ "@unocss/astro": "^66.7.0",
48
+ "@unocss/reset": "66.7.0",
49
+ "@waline/client": "^3.13.0",
50
+ "astro": "^6.3.7",
51
+ "astro-compress": "^2.4.1",
52
+ "astro-og-canvas": "^0.11.1",
53
+ "astro-pagefind": "^2.0.0",
54
+ "canvaskit-wasm": "^0.41.1",
55
+ "feed": "^5.2.1",
56
+ "katex": "^0.17.0",
57
+ "lite-youtube-embed": "^0.3.4",
58
+ "markdown-it": "^14.1.1",
59
+ "mdast-util-to-string": "^4.0.0",
60
+ "mermaid": "^11.15.0",
61
+ "node-html-parser": "^7.1.0",
62
+ "playwright": "^1.60.0",
63
+ "reading-time": "^1.5.0",
64
+ "rehype-katex": "^7.0.1",
65
+ "rehype-mermaid": "^3.0.0",
66
+ "rehype-slug": "^6.0.0",
67
+ "remark-directive": "^4.0.0",
68
+ "remark-math": "^6.0.0",
69
+ "sanitize-html": "^2.17.4",
70
+ "sharp": "^0.34.5",
71
+ "twikoo": "^1.7.9",
72
+ "unist-util-visit": "^5.1.0",
73
+ "unocss": "66.7.0",
74
+ "unocss-preset-theme": "^0.14.1",
75
+ "yaml": "^2.9.0",
76
+ "zod": "^3.25.76"
77
+ },
78
+ "devDependencies": {
79
+ "@antfu/eslint-config": "^9.0.0",
80
+ "@astrojs/check": "^0.9.9",
81
+ "@playwright/test": "^1.60.0",
82
+ "@types/markdown-it": "^14.1.2",
83
+ "@types/node": "^25.9.1",
84
+ "@types/sanitize-html": "^2.16.1",
85
+ "@unocss/eslint-plugin": "66.7.0",
86
+ "@unocss/preset-attributify": "66.7.0",
87
+ "astro-eslint-parser": "^1.4.0",
88
+ "autocorrect-node": "^2.14.0",
89
+ "eslint": "^10.4.0",
90
+ "eslint-plugin-astro": "^1.7.0",
91
+ "fast-glob": "^3.3.3",
92
+ "lint-staged": "^17.0.5",
93
+ "opentype.js": "^2.0.0",
94
+ "simple-git-hooks": "^2.13.1",
95
+ "tsx": "^4.22.3",
96
+ "typescript": "~5.9.3"
97
+ },
98
+ "pnpm": {},
99
+ "simple-git-hooks": {
100
+ "pre-commit": "pnpm lint-staged"
101
+ },
102
+ "lint-staged": {
103
+ "*.{js,mjs,ts,astro}": "eslint --fix"
104
+ }
105
+ }
@@ -0,0 +1,98 @@
1
+ diff --git a/integration/index.mjs b/integration/index.mjs
2
+ index f62d2c1f0f983ac6220716ea27edf453e1814672..a559b30a40f788fc6e84f29df0ffcc5c9ba0d444 100644
3
+ --- a/integration/index.mjs
4
+ +++ b/integration/index.mjs
5
+ @@ -1,4 +1,4 @@
6
+ -const PartytownSnippet = "/* Partytown 0.11.2 - MIT QwikDev */\nconst t={preserveBehavior:!1},e=e=>{if(\"string\"==typeof e)return[e,t];const[n,r=t]=e;return[n,{...t,...r}]},n=Object.freeze((t=>{const e=new Set;let n=[];do{Object.getOwnPropertyNames(n).forEach((t=>{\"function\"==typeof n[t]&&e.add(t)}))}while((n=Object.getPrototypeOf(n))!==Object.prototype);return Array.from(e)})());!function(t,r,o,i,a,s,c,l,d,p,u=t,f){function h(){f||(f=1,\"/\"==(c=(s.lib||\"/~partytown/\")+(s.debug?\"debug/\":\"\"))[0]&&(d=r.querySelectorAll('script[type=\"text/partytown\"]'),i!=t?i.dispatchEvent(new CustomEvent(\"pt1\",{detail:t})):(l=setTimeout(v,(null==s?void 0:s.fallbackTimeout)||1e4),r.addEventListener(\"pt0\",w),a?y(1):o.serviceWorker?o.serviceWorker.register(c+(s.swPath||\"partytown-sw.js\"),{scope:c}).then((function(t){t.active?y():t.installing&&t.installing.addEventListener(\"statechange\",(function(t){\"activated\"==t.target.state&&y()}))}),console.error):v())))}function y(e){p=r.createElement(e?\"script\":\"iframe\"),t._pttab=Date.now(),e||(p.style.display=\"block\",p.style.width=\"0\",p.style.height=\"0\",p.style.border=\"0\",p.style.visibility=\"hidden\",p.setAttribute(\"aria-hidden\",!0)),p.src=c+\"partytown-\"+(e?\"atomics.js?v=0.11.2\":\"sandbox-sw.html?\"+t._pttab),r.querySelector(s.sandboxParent||\"body\").appendChild(p)}function v(n,o){for(w(),i==t&&(s.forward||[]).map((function(n){const[r]=e(n);delete t[r.split(\".\")[0]]})),n=0;n<d.length;n++)(o=r.createElement(\"script\")).innerHTML=d[n].innerHTML,o.nonce=s.nonce,r.head.appendChild(o);p&&p.parentNode.removeChild(p)}function w(){clearTimeout(l)}s=t.partytown||{},i==t&&(s.forward||[]).map((function(r){const[o,{preserveBehavior:i}]=e(r);u=t,o.split(\".\").map((function(e,r,o){var a;u=u[o[r]]=r+1<o.length?u[o[r]]||(a=o[r+1],n.includes(a)?[]:{}):(()=>{let e=null;if(i){const{methodOrProperty:n,thisObject:r}=((t,e)=>{let n=t;for(let t=0;t<e.length-1;t+=1)n=n[e[t]];return{thisObject:n,methodOrProperty:e.length>0?n[e[e.length-1]]:void 0}})(t,o);\"function\"==typeof n&&(e=(...t)=>n.apply(r,...t))}return function(){let n;return e&&(n=e(arguments)),(t._ptf=t._ptf||[]).push(o,arguments),n}})()}))})),\"complete\"==r.readyState?h():(t.addEventListener(\"DOMContentLoaded\",h),t.addEventListener(\"load\",h))}(window,document,navigator,top,window.crossOriginIsolated);";
7
+ +const PartytownSnippet = "/* Partytown 0.11.2 - MIT QwikDev */\nconst tp={preserveBehavior:!1},ep=e=>{if(\"string\"==typeof e)return[e,tp];const[n,r=tp]=e;return[n,{...tp,...r}]},n=Object.freeze((t=>{const e=new Set;let n=[];do{Object.getOwnPropertyNames(n).forEach((t=>{\"function\"==typeof n[t]&&e.add(t)}))}while((n=Object.getPrototypeOf(n))!==Object.prototype);return Array.from(e)})());!function(t,r,o,i,a,s,c,l,d,p,u=t,f){function h(){f||(f=1,\"/\"==(c=(s.lib||\"/~partytown/\")+(s.debug?\"debug/\":\"\"))[0]&&(d=r.querySelectorAll('script[type=\"text/partytown\"]'),i!=t?i.dispatchEvent(new CustomEvent(\"pt1\",{detail:t})):(l=setTimeout(v,(null==s?void 0:s.fallbackTimeout)||1e4),r.addEventListener(\"pt0\",w),a?y(1):o.serviceWorker?o.serviceWorker.register(c+(s.swPath||\"partytown-sw.js\"),{scope:c}).then((function(t){t.active?y():t.installing&&t.installing.addEventListener(\"statechange\",(function(t){\"activated\"==t.target.state&&y()}))}),console.error):v())))}function y(e){p=r.createElement(e?\"script\":\"iframe\"),t._pttab=Date.now(),e||(p.style.display=\"block\",p.style.width=\"0\",p.style.height=\"0\",p.style.border=\"0\",p.style.visibility=\"hidden\",p.setAttribute(\"aria-hidden\",!0)),p.src=c+\"partytown-\"+(e?\"atomics.js?v=0.11.2\":\"sandbox-sw.html?\"+t._pttab),r.querySelector(s.sandboxParent||\"body\").appendChild(p)}function v(n,o){for(w(),i==t&&(s.forward||[]).map((function(n){const[r]=ep(n);delete t[r.split(\".\")[0]]})),n=0;n<d.length;n++)(o=r.createElement(\"script\")).innerHTML=d[n].innerHTML,o.nonce=s.nonce,r.head.appendChild(o);p&&p.parentNode.removeChild(p)}function w(){clearTimeout(l)}s=t.partytown||{},i==t&&(s.forward||[]).map((function(r){const[o,{preserveBehavior:i}]=ep(r);u=t,o.split(\".\").map((function(e,r,o){var a;u=u[o[r]]=r+1<o.length?u[o[r]]||(a=o[r+1],n.includes(a)?[]:{}):(()=>{let e=null;if(i){const{methodOrProperty:n,thisObject:r}=((t,e)=>{let n=t;for(let t=0;t<e.length-1;t+=1)n=n[e[t]];return{thisObject:n,methodOrProperty:e.length>0?n[e[e.length-1]]:void 0}})(t,o);\"function\"==typeof n&&(e=(...t)=>n.apply(r,...t))}return function(){let n;return e&&(n=e(arguments)),(t._ptf=t._ptf||[]).push(o,arguments),n}})()}))})),\"complete\"==r.readyState?h():(t.addEventListener(\"DOMContentLoaded\",h),t.addEventListener(\"load\",h))}(window,document,navigator,top,window.crossOriginIsolated);";
8
+
9
+ /**
10
+ * The `type` attribute for Partytown scripts, which does two things:
11
+ diff --git a/lib/partytown.js b/lib/partytown.js
12
+ index ee7034c7875950681205c89580f4d1513df1e385..5f61af194420ceb72fab9ada44fce4f744ab808b 100644
13
+ --- a/lib/partytown.js
14
+ +++ b/lib/partytown.js
15
+ @@ -1,2 +1,80 @@
16
+ -/* Partytown 0.11.2 - MIT QwikDev */
17
+ -const t={preserveBehavior:!1},e=e=>{if("string"==typeof e)return[e,t];const[n,r=t]=e;return[n,{...t,...r}]},n=Object.freeze((t=>{const e=new Set;let n=[];do{Object.getOwnPropertyNames(n).forEach((t=>{"function"==typeof n[t]&&e.add(t)}))}while((n=Object.getPrototypeOf(n))!==Object.prototype);return Array.from(e)})());!function(t,r,o,i,a,s,c,l,d,p,u=t,f){function h(){f||(f=1,"/"==(c=(s.lib||"/~partytown/")+(s.debug?"debug/":""))[0]&&(d=r.querySelectorAll('script[type="text/partytown"]'),i!=t?i.dispatchEvent(new CustomEvent("pt1",{detail:t})):(l=setTimeout(v,(null==s?void 0:s.fallbackTimeout)||1e4),r.addEventListener("pt0",w),a?y(1):o.serviceWorker?o.serviceWorker.register(c+(s.swPath||"partytown-sw.js"),{scope:c}).then((function(t){t.active?y():t.installing&&t.installing.addEventListener("statechange",(function(t){"activated"==t.target.state&&y()}))}),console.error):v())))}function y(e){p=r.createElement(e?"script":"iframe"),t._pttab=Date.now(),e||(p.style.display="block",p.style.width="0",p.style.height="0",p.style.border="0",p.style.visibility="hidden",p.setAttribute("aria-hidden",!0)),p.src=c+"partytown-"+(e?"atomics.js?v=0.11.2":"sandbox-sw.html?"+t._pttab),r.querySelector(s.sandboxParent||"body").appendChild(p)}function v(n,o){for(w(),i==t&&(s.forward||[]).map((function(n){const[r]=e(n);delete t[r.split(".")[0]]})),n=0;n<d.length;n++)(o=r.createElement("script")).innerHTML=d[n].innerHTML,o.nonce=s.nonce,r.head.appendChild(o);p&&p.parentNode.removeChild(p)}function w(){clearTimeout(l)}s=t.partytown||{},i==t&&(s.forward||[]).map((function(r){const[o,{preserveBehavior:i}]=e(r);u=t,o.split(".").map((function(e,r,o){var a;u=u[o[r]]=r+1<o.length?u[o[r]]||(a=o[r+1],n.includes(a)?[]:{}):(()=>{let e=null;if(i){const{methodOrProperty:n,thisObject:r}=((t,e)=>{let n=t;for(let t=0;t<e.length-1;t+=1)n=n[e[t]];return{thisObject:n,methodOrProperty:e.length>0?n[e[e.length-1]]:void 0}})(t,o);"function"==typeof n&&(e=(...t)=>n.apply(r,...t))}return function(){let n;return e&&(n=e(arguments)),(t._ptf=t._ptf||[]).push(o,arguments),n}})()}))})),"complete"==r.readyState?h():(t.addEventListener("DOMContentLoaded",h),t.addEventListener("load",h))}(window,document,navigator,top,window.crossOriginIsolated);
18
+
19
+ +/* Partytown 0.11.2 - MIT QwikDev */
20
+ +const tg = {
21
+ + preserveBehavior: !1
22
+ +},
23
+ + eg = e => {
24
+ + if ("string" == typeof e) return [e, tg];
25
+ + const [n, r = tg] = e;
26
+ + return [n, {
27
+ + ...tg,
28
+ + ...r
29
+ + }]
30
+ + },
31
+ + n = Object.freeze((t => {
32
+ + const e = new Set;
33
+ + let n = [];
34
+ + do {
35
+ + Object.getOwnPropertyNames(n).forEach((t => {
36
+ + "function" == typeof n[t] && e.add(t)
37
+ + }))
38
+ + } while ((n = Object.getPrototypeOf(n)) !== Object.prototype);
39
+ + return Array.from(e)
40
+ + })());
41
+ +! function (t, r, o, i, a, s, c, l, d, p, u = t, f) {
42
+ + function h() {
43
+ + f || (f = 1, "/" == (c = (s.lib || "/~partytown/") + (s.debug ? "debug/" : ""))[0] && (d = r.querySelectorAll('script[type="text/partytown"]'), i != t ? i.dispatchEvent(new CustomEvent("pt1", {
44
+ + detail: t
45
+ + })) : (l = setTimeout(v, (null == s ? void 0 : s.fallbackTimeout) || 1e4), r.addEventListener("pt0", w), a ? y(1) : o.serviceWorker ? o.serviceWorker.register(c + (s.swPath || "partytown-sw.js"), {
46
+ + scope: c
47
+ + }).then((function (t) {
48
+ + t.active ? y() : t.installing && t.installing.addEventListener("statechange", (function (t) {
49
+ + "activated" == t.target.state && y()
50
+ + }))
51
+ + }), console.error) : v())))
52
+ + }
53
+ +
54
+ + function y(e) {
55
+ + p = r.createElement(e ? "script" : "iframe"), t._pttab = Date.now(), e || (p.style.display = "block", p.style.width = "0", p.style.height = "0", p.style.border = "0", p.style.visibility = "hidden", p.setAttribute("aria-hidden", !0)), p.src = c + "partytown-" + (e ? "atomics.js?v=0.11.2" : "sandbox-sw.html?" + t._pttab), r.querySelector(s.sandboxParent || "body").appendChild(p)
56
+ + }
57
+ +
58
+ + function v(n, o) {
59
+ + for (w(), i == t && (s.forward || []).map((function (n) {
60
+ + const [r] = eg(n);
61
+ + delete t[r.split(".")[0]]
62
+ + })), n = 0; n < d.length; n++)(o = r.createElement("script")).innerHTML = d[n].innerHTML, o.nonce = s.nonce, r.head.appendChild(o);
63
+ + p && p.parentNode.removeChild(p)
64
+ + }
65
+ +
66
+ + function w() {
67
+ + clearTimeout(l)
68
+ + }
69
+ + s = t.partytown || {}, i == t && (s.forward || []).map((function (r) {
70
+ + const [o, {
71
+ + preserveBehavior: i
72
+ + }] = eg(r);
73
+ + u = t, o.split(".").map((function (e, r, o) {
74
+ + var a;
75
+ + u = u[o[r]] = r + 1 < o.length ? u[o[r]] || (a = o[r + 1], n.includes(a) ? [] : {}) : (() => {
76
+ + let e = null;
77
+ + if (i) {
78
+ + const {
79
+ + methodOrProperty: n,
80
+ + thisObject: r
81
+ + } = ((t, e) => {
82
+ + let n = t;
83
+ + for (let t = 0; t < e.length - 1; t += 1) n = n[e[t]];
84
+ + return {
85
+ + thisObject: n,
86
+ + methodOrProperty: e.length > 0 ? n[e[e.length - 1]] : void 0
87
+ + }
88
+ + })(t, o);
89
+ + "function" == typeof n && (e = (...t) => n.apply(r, ...t))
90
+ + }
91
+ + return function () {
92
+ + let n;
93
+ + return e && (n = e(arguments)), (t._ptf = t._ptf || []).push(o, arguments), n
94
+ + }
95
+ + })()
96
+ + }))
97
+ + })), "complete" == r.readyState ? h() : (t.addEventListener("DOMContentLoaded", h), t.addEventListener("load", h))
98
+ +}(window, document, navigator, top, window.crossOriginIsolated);