@kirchdev/duxt 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 (401) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -0
  3. package/app/app.config.ts +13 -0
  4. package/app/app.vue +122 -0
  5. package/app/assets/css/duxt.css +555 -0
  6. package/app/assets/css/typeset.css +498 -0
  7. package/app/components/DuxtBrand.vue +53 -0
  8. package/app/components/DuxtBreadcrumb.vue +37 -0
  9. package/app/components/DuxtCodeBlock.vue +143 -0
  10. package/app/components/DuxtCopyPage.vue +117 -0
  11. package/app/components/DuxtFooter.vue +86 -0
  12. package/app/components/DuxtHeader.vue +496 -0
  13. package/app/components/DuxtJsonEditor.vue +359 -0
  14. package/app/components/DuxtLandingProse.vue +90 -0
  15. package/app/components/DuxtLandingShowcase.vue +295 -0
  16. package/app/components/DuxtLiveWindow.vue +343 -0
  17. package/app/components/DuxtLocale.vue +86 -0
  18. package/app/components/DuxtMetaList.vue +76 -0
  19. package/app/components/DuxtNavigation.vue +94 -0
  20. package/app/components/DuxtNavigationLink.vue +41 -0
  21. package/app/components/DuxtOpenApiCallbacks.vue +92 -0
  22. package/app/components/DuxtOpenApiClient.vue +1339 -0
  23. package/app/components/DuxtOpenApiMedia.vue +108 -0
  24. package/app/components/DuxtOpenApiMethod.vue +59 -0
  25. package/app/components/DuxtOpenApiParameters.vue +114 -0
  26. package/app/components/DuxtOpenApiResponses.vue +109 -0
  27. package/app/components/DuxtOpenApiSchema.vue +365 -0
  28. package/app/components/DuxtOpenApiSecurity.vue +115 -0
  29. package/app/components/DuxtPageFeedback.vue +56 -0
  30. package/app/components/DuxtPageInfo.vue +194 -0
  31. package/app/components/DuxtPageNav.vue +93 -0
  32. package/app/components/DuxtPreviewSkeleton.vue +262 -0
  33. package/app/components/DuxtProgress.vue +45 -0
  34. package/app/components/DuxtSearch.vue +271 -0
  35. package/app/components/DuxtSections.vue +65 -0
  36. package/app/components/DuxtShortcuts.vue +46 -0
  37. package/app/components/DuxtSkipLink.vue +34 -0
  38. package/app/components/DuxtToc.vue +86 -0
  39. package/app/components/DuxtTranslationBanner.vue +64 -0
  40. package/app/components/DuxtVersion.vue +168 -0
  41. package/app/components/DuxtVersionBanner.vue +109 -0
  42. package/app/components/OgImage/Duxt.satori.vue +82 -0
  43. package/app/components/content/Accordion.vue +37 -0
  44. package/app/components/content/AccordionItem.vue +46 -0
  45. package/app/components/content/Callout.vue +58 -0
  46. package/app/components/content/ChangelogGroup.vue +91 -0
  47. package/app/components/content/ChangelogReleases.vue +520 -0
  48. package/app/components/content/CodeGroup.vue +183 -0
  49. package/app/components/content/DevtoolsPanel.vue +107 -0
  50. package/app/components/content/Field.vue +50 -0
  51. package/app/components/content/FieldGroup.vue +8 -0
  52. package/app/components/content/FileTree.vue +92 -0
  53. package/app/components/content/Mermaid.vue +70 -0
  54. package/app/components/content/OpenApiOperation.vue +185 -0
  55. package/app/components/content/OpenApiOperations.vue +72 -0
  56. package/app/components/content/OpenApiOverview.vue +302 -0
  57. package/app/components/content/PackageManagers.vue +187 -0
  58. package/app/components/content/PageCards.vue +48 -0
  59. package/app/components/content/Partial.vue +65 -0
  60. package/app/components/content/Preview.vue +173 -0
  61. package/app/components/content/ProseA.vue +55 -0
  62. package/app/components/content/ProseH2.vue +53 -0
  63. package/app/components/content/ProseH3.vue +53 -0
  64. package/app/components/content/ProseH4.vue +53 -0
  65. package/app/components/content/ProseImg.vue +87 -0
  66. package/app/components/content/ProsePre.vue +58 -0
  67. package/app/components/content/ProseTable.vue +11 -0
  68. package/app/components/content/Since.vue +21 -0
  69. package/app/components/content/Steps.vue +20 -0
  70. package/app/components/content/Tab.vue +23 -0
  71. package/app/components/content/TabGroup.vue +58 -0
  72. package/app/components/ui/alert/Alert.vue +21 -0
  73. package/app/components/ui/alert/AlertDescription.vue +22 -0
  74. package/app/components/ui/alert/AlertTitle.vue +22 -0
  75. package/app/components/ui/alert/index.ts +24 -0
  76. package/app/components/ui/badge/Badge.vue +28 -0
  77. package/app/components/ui/badge/index.ts +52 -0
  78. package/app/components/ui/breadcrumb/Breadcrumb.vue +13 -0
  79. package/app/components/ui/breadcrumb/BreadcrumbEllipsis.vue +22 -0
  80. package/app/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
  81. package/app/components/ui/breadcrumb/BreadcrumbLink.vue +24 -0
  82. package/app/components/ui/breadcrumb/BreadcrumbList.vue +22 -0
  83. package/app/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
  84. package/app/components/ui/breadcrumb/BreadcrumbSeparator.vue +21 -0
  85. package/app/components/ui/breadcrumb/index.ts +7 -0
  86. package/app/components/ui/button/Button.vue +31 -0
  87. package/app/components/ui/button/index.ts +39 -0
  88. package/app/components/ui/card/Card.vue +22 -0
  89. package/app/components/ui/card/CardAction.vue +22 -0
  90. package/app/components/ui/card/CardContent.vue +14 -0
  91. package/app/components/ui/card/CardDescription.vue +17 -0
  92. package/app/components/ui/card/CardFooter.vue +17 -0
  93. package/app/components/ui/card/CardHeader.vue +22 -0
  94. package/app/components/ui/card/CardTitle.vue +17 -0
  95. package/app/components/ui/card/index.ts +7 -0
  96. package/app/components/ui/collapsible/Collapsible.vue +19 -0
  97. package/app/components/ui/collapsible/CollapsibleContent.vue +12 -0
  98. package/app/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
  99. package/app/components/ui/collapsible/index.ts +3 -0
  100. package/app/components/ui/command/Command.vue +98 -0
  101. package/app/components/ui/command/CommandDialog.vue +42 -0
  102. package/app/components/ui/command/CommandEmpty.vue +31 -0
  103. package/app/components/ui/command/CommandGroup.vue +52 -0
  104. package/app/components/ui/command/CommandInput.vue +45 -0
  105. package/app/components/ui/command/CommandItem.vue +85 -0
  106. package/app/components/ui/command/CommandList.vue +32 -0
  107. package/app/components/ui/command/CommandSeparator.vue +23 -0
  108. package/app/components/ui/command/CommandShortcut.vue +19 -0
  109. package/app/components/ui/command/index.ts +29 -0
  110. package/app/components/ui/dialog/Dialog.vue +15 -0
  111. package/app/components/ui/dialog/DialogClose.vue +12 -0
  112. package/app/components/ui/dialog/DialogContent.vue +61 -0
  113. package/app/components/ui/dialog/DialogDescription.vue +25 -0
  114. package/app/components/ui/dialog/DialogFooter.vue +30 -0
  115. package/app/components/ui/dialog/DialogHeader.vue +17 -0
  116. package/app/components/ui/dialog/DialogOverlay.vue +28 -0
  117. package/app/components/ui/dialog/DialogScrollContent.vue +65 -0
  118. package/app/components/ui/dialog/DialogTitle.vue +25 -0
  119. package/app/components/ui/dialog/DialogTrigger.vue +12 -0
  120. package/app/components/ui/dialog/index.ts +10 -0
  121. package/app/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
  122. package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +47 -0
  123. package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +47 -0
  124. package/app/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
  125. package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
  126. package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +27 -0
  127. package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
  128. package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +48 -0
  129. package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
  130. package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +19 -0
  131. package/app/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
  132. package/app/components/ui/dropdown-menu/DropdownMenuSubContent.vue +34 -0
  133. package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +34 -0
  134. package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
  135. package/app/components/ui/dropdown-menu/index.ts +16 -0
  136. package/app/components/ui/input/Input.vue +35 -0
  137. package/app/components/ui/input/index.ts +1 -0
  138. package/app/components/ui/navigation-menu/NavigationMenu.vue +42 -0
  139. package/app/components/ui/navigation-menu/NavigationMenuContent.vue +35 -0
  140. package/app/components/ui/navigation-menu/NavigationMenuIndicator.vue +32 -0
  141. package/app/components/ui/navigation-menu/NavigationMenuItem.vue +23 -0
  142. package/app/components/ui/navigation-menu/NavigationMenuLink.vue +30 -0
  143. package/app/components/ui/navigation-menu/NavigationMenuList.vue +30 -0
  144. package/app/components/ui/navigation-menu/NavigationMenuTrigger.vue +31 -0
  145. package/app/components/ui/navigation-menu/NavigationMenuViewport.vue +30 -0
  146. package/app/components/ui/navigation-menu/index.ts +14 -0
  147. package/app/components/ui/scroll-area/ScrollArea.vue +31 -0
  148. package/app/components/ui/scroll-area/ScrollBar.vue +38 -0
  149. package/app/components/ui/scroll-area/index.ts +2 -0
  150. package/app/components/ui/select/Select.vue +15 -0
  151. package/app/components/ui/select/SelectContent.vue +59 -0
  152. package/app/components/ui/select/SelectGroup.vue +19 -0
  153. package/app/components/ui/select/SelectItem.vue +42 -0
  154. package/app/components/ui/select/SelectItemText.vue +12 -0
  155. package/app/components/ui/select/SelectLabel.vue +16 -0
  156. package/app/components/ui/select/SelectScrollDownButton.vue +28 -0
  157. package/app/components/ui/select/SelectScrollUpButton.vue +28 -0
  158. package/app/components/ui/select/SelectSeparator.vue +20 -0
  159. package/app/components/ui/select/SelectTrigger.vue +32 -0
  160. package/app/components/ui/select/SelectValue.vue +12 -0
  161. package/app/components/ui/select/index.ts +11 -0
  162. package/app/components/ui/separator/Separator.vue +30 -0
  163. package/app/components/ui/separator/index.ts +1 -0
  164. package/app/components/ui/sheet/Sheet.vue +15 -0
  165. package/app/components/ui/sheet/SheetClose.vue +12 -0
  166. package/app/components/ui/sheet/SheetContent.vue +68 -0
  167. package/app/components/ui/sheet/SheetDescription.vue +23 -0
  168. package/app/components/ui/sheet/SheetFooter.vue +15 -0
  169. package/app/components/ui/sheet/SheetHeader.vue +15 -0
  170. package/app/components/ui/sheet/SheetOverlay.vue +28 -0
  171. package/app/components/ui/sheet/SheetTitle.vue +23 -0
  172. package/app/components/ui/sheet/SheetTrigger.vue +12 -0
  173. package/app/components/ui/sheet/index.ts +8 -0
  174. package/app/components/ui/sidebar/Sidebar.vue +110 -0
  175. package/app/components/ui/sidebar/SidebarContent.vue +23 -0
  176. package/app/components/ui/sidebar/SidebarFooter.vue +18 -0
  177. package/app/components/ui/sidebar/SidebarGroup.vue +18 -0
  178. package/app/components/ui/sidebar/SidebarGroupAction.vue +31 -0
  179. package/app/components/ui/sidebar/SidebarGroupContent.vue +18 -0
  180. package/app/components/ui/sidebar/SidebarGroupLabel.vue +30 -0
  181. package/app/components/ui/sidebar/SidebarHeader.vue +18 -0
  182. package/app/components/ui/sidebar/SidebarInput.vue +19 -0
  183. package/app/components/ui/sidebar/SidebarInset.vue +23 -0
  184. package/app/components/ui/sidebar/SidebarMenu.vue +18 -0
  185. package/app/components/ui/sidebar/SidebarMenuAction.vue +42 -0
  186. package/app/components/ui/sidebar/SidebarMenuBadge.vue +28 -0
  187. package/app/components/ui/sidebar/SidebarMenuButton.vue +60 -0
  188. package/app/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
  189. package/app/components/ui/sidebar/SidebarMenuItem.vue +18 -0
  190. package/app/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
  191. package/app/components/ui/sidebar/SidebarMenuSub.vue +24 -0
  192. package/app/components/ui/sidebar/SidebarMenuSubButton.vue +43 -0
  193. package/app/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
  194. package/app/components/ui/sidebar/SidebarProvider.vue +109 -0
  195. package/app/components/ui/sidebar/SidebarRail.vue +35 -0
  196. package/app/components/ui/sidebar/SidebarSeparator.vue +19 -0
  197. package/app/components/ui/sidebar/SidebarTrigger.vue +26 -0
  198. package/app/components/ui/sidebar/index.ts +62 -0
  199. package/app/components/ui/sidebar/utils.ts +19 -0
  200. package/app/components/ui/skeleton/Skeleton.vue +17 -0
  201. package/app/components/ui/skeleton/index.ts +1 -0
  202. package/app/components/ui/sonner/Sonner.vue +67 -0
  203. package/app/components/ui/sonner/index.ts +1 -0
  204. package/app/components/ui/tabs/Tabs.vue +26 -0
  205. package/app/components/ui/tabs/TabsContent.vue +23 -0
  206. package/app/components/ui/tabs/TabsList.vue +28 -0
  207. package/app/components/ui/tabs/TabsTrigger.vue +30 -0
  208. package/app/components/ui/tabs/index.ts +4 -0
  209. package/app/components/ui/textarea/Textarea.vue +32 -0
  210. package/app/components/ui/textarea/index.ts +1 -0
  211. package/app/components/ui/tooltip/Tooltip.vue +15 -0
  212. package/app/components/ui/tooltip/TooltipContent.vue +49 -0
  213. package/app/components/ui/tooltip/TooltipProvider.vue +14 -0
  214. package/app/components/ui/tooltip/TooltipTrigger.vue +12 -0
  215. package/app/components/ui/tooltip/index.ts +4 -0
  216. package/app/composables/useActiveHeading.ts +90 -0
  217. package/app/composables/useDuxtAnimatedHeight.ts +155 -0
  218. package/app/composables/useDuxtBreadcrumb.ts +60 -0
  219. package/app/composables/useDuxtChoice.ts +28 -0
  220. package/app/composables/useDuxtCollection.ts +89 -0
  221. package/app/composables/useDuxtConfig.ts +47 -0
  222. package/app/composables/useDuxtNavigation.ts +87 -0
  223. package/app/composables/useDuxtPageFocus.ts +30 -0
  224. package/app/composables/useDuxtPath.ts +42 -0
  225. package/app/composables/useDuxtSearch.ts +208 -0
  226. package/app/composables/useDuxtSection.ts +44 -0
  227. package/app/composables/useDuxtSectionRow.ts +49 -0
  228. package/app/composables/useDuxtShortcuts.ts +58 -0
  229. package/app/composables/useDuxtSiteUrl.ts +27 -0
  230. package/app/composables/useDuxtToast.ts +25 -0
  231. package/app/composables/useDuxtVersion.ts +138 -0
  232. package/app/composables/useFuzzySearch.ts +83 -0
  233. package/app/composables/usePackageManager.ts +4 -0
  234. package/app/composables/useRecentPages.ts +52 -0
  235. package/app/error.vue +133 -0
  236. package/app/layouts/changelog.vue +85 -0
  237. package/app/layouts/default.vue +23 -0
  238. package/app/layouts/docs.vue +62 -0
  239. package/app/layouts/reference.vue +85 -0
  240. package/app/lib/utils.ts +7 -0
  241. package/app/middleware/duxt-section-layout.global.ts +57 -0
  242. package/app/pages/[...slug].vue +441 -0
  243. package/app/pages/index.vue +479 -0
  244. package/app/types/duxt.d.ts +955 -0
  245. package/app/utils/aside.ts +16 -0
  246. package/app/utils/changelog.ts +182 -0
  247. package/app/utils/code-highlight.ts +116 -0
  248. package/app/utils/duxt-config.ts +185 -0
  249. package/app/utils/duxt-text.ts +135 -0
  250. package/app/utils/file-icons.ts +62 -0
  251. package/app/utils/generated-sections.ts +190 -0
  252. package/app/utils/generated-toc.ts +216 -0
  253. package/app/utils/heading-link.ts +32 -0
  254. package/app/utils/locale-path.ts +45 -0
  255. package/app/utils/navigation-tree.ts +206 -0
  256. package/app/utils/nearest-page.ts +72 -0
  257. package/app/utils/openapi.ts +1028 -0
  258. package/app/utils/package-command.ts +162 -0
  259. package/app/utils/request-samples.ts +635 -0
  260. package/app/utils/section-scope.ts +97 -0
  261. package/app/utils/sheet-navigation.ts +100 -0
  262. package/app/utils/source-file.ts +84 -0
  263. package/app/utils/version-choices.ts +68 -0
  264. package/app/utils/version-paths.ts +65 -0
  265. package/bin/duxt-report.mjs +45 -0
  266. package/components.json +19 -0
  267. package/content-cache.ts +193 -0
  268. package/content.config.ts +38 -0
  269. package/duxt-app-config.ts +124 -0
  270. package/highlight-langs.ts +68 -0
  271. package/i18n/i18n.config.ts +16 -0
  272. package/i18n/locales/de/duxt/changelog.json +17 -0
  273. package/i18n/locales/de/duxt/code.json +13 -0
  274. package/i18n/locales/de/duxt/defaults.json +22 -0
  275. package/i18n/locales/de/duxt/devtools.json +7 -0
  276. package/i18n/locales/de/duxt/error.json +10 -0
  277. package/i18n/locales/de/duxt/footer.json +7 -0
  278. package/i18n/locales/de/duxt/locale.json +7 -0
  279. package/i18n/locales/de/duxt/nav.json +17 -0
  280. package/i18n/locales/de/duxt/openapi.json +89 -0
  281. package/i18n/locales/de/duxt/page.json +34 -0
  282. package/i18n/locales/de/duxt/search.json +12 -0
  283. package/i18n/locales/de/duxt/shortcuts.json +11 -0
  284. package/i18n/locales/de/duxt/theme.json +7 -0
  285. package/i18n/locales/de/duxt/toc.json +7 -0
  286. package/i18n/locales/de/duxt/version.json +25 -0
  287. package/i18n/locales/en/duxt/changelog.json +17 -0
  288. package/i18n/locales/en/duxt/code.json +13 -0
  289. package/i18n/locales/en/duxt/defaults.json +22 -0
  290. package/i18n/locales/en/duxt/devtools.json +7 -0
  291. package/i18n/locales/en/duxt/error.json +10 -0
  292. package/i18n/locales/en/duxt/footer.json +7 -0
  293. package/i18n/locales/en/duxt/locale.json +7 -0
  294. package/i18n/locales/en/duxt/nav.json +17 -0
  295. package/i18n/locales/en/duxt/openapi.json +89 -0
  296. package/i18n/locales/en/duxt/page.json +34 -0
  297. package/i18n/locales/en/duxt/search.json +12 -0
  298. package/i18n/locales/en/duxt/shortcuts.json +11 -0
  299. package/i18n/locales/en/duxt/theme.json +7 -0
  300. package/i18n/locales/en/duxt/toc.json +7 -0
  301. package/i18n/locales/en/duxt/version.json +25 -0
  302. package/i18n/locales/es/duxt/changelog.json +17 -0
  303. package/i18n/locales/es/duxt/code.json +13 -0
  304. package/i18n/locales/es/duxt/defaults.json +22 -0
  305. package/i18n/locales/es/duxt/devtools.json +7 -0
  306. package/i18n/locales/es/duxt/error.json +10 -0
  307. package/i18n/locales/es/duxt/footer.json +7 -0
  308. package/i18n/locales/es/duxt/locale.json +7 -0
  309. package/i18n/locales/es/duxt/nav.json +17 -0
  310. package/i18n/locales/es/duxt/openapi.json +89 -0
  311. package/i18n/locales/es/duxt/page.json +34 -0
  312. package/i18n/locales/es/duxt/search.json +12 -0
  313. package/i18n/locales/es/duxt/shortcuts.json +11 -0
  314. package/i18n/locales/es/duxt/theme.json +7 -0
  315. package/i18n/locales/es/duxt/toc.json +7 -0
  316. package/i18n/locales/es/duxt/version.json +25 -0
  317. package/i18n/locales/fr/duxt/changelog.json +17 -0
  318. package/i18n/locales/fr/duxt/code.json +13 -0
  319. package/i18n/locales/fr/duxt/defaults.json +22 -0
  320. package/i18n/locales/fr/duxt/devtools.json +7 -0
  321. package/i18n/locales/fr/duxt/error.json +10 -0
  322. package/i18n/locales/fr/duxt/footer.json +7 -0
  323. package/i18n/locales/fr/duxt/locale.json +7 -0
  324. package/i18n/locales/fr/duxt/nav.json +17 -0
  325. package/i18n/locales/fr/duxt/openapi.json +89 -0
  326. package/i18n/locales/fr/duxt/page.json +34 -0
  327. package/i18n/locales/fr/duxt/search.json +12 -0
  328. package/i18n/locales/fr/duxt/shortcuts.json +11 -0
  329. package/i18n/locales/fr/duxt/theme.json +7 -0
  330. package/i18n/locales/fr/duxt/toc.json +7 -0
  331. package/i18n/locales/fr/duxt/version.json +25 -0
  332. package/i18n/locales/pt/duxt/changelog.json +17 -0
  333. package/i18n/locales/pt/duxt/code.json +13 -0
  334. package/i18n/locales/pt/duxt/defaults.json +22 -0
  335. package/i18n/locales/pt/duxt/devtools.json +7 -0
  336. package/i18n/locales/pt/duxt/error.json +10 -0
  337. package/i18n/locales/pt/duxt/footer.json +7 -0
  338. package/i18n/locales/pt/duxt/locale.json +7 -0
  339. package/i18n/locales/pt/duxt/nav.json +17 -0
  340. package/i18n/locales/pt/duxt/openapi.json +89 -0
  341. package/i18n/locales/pt/duxt/page.json +34 -0
  342. package/i18n/locales/pt/duxt/search.json +12 -0
  343. package/i18n/locales/pt/duxt/shortcuts.json +11 -0
  344. package/i18n/locales/pt/duxt/theme.json +7 -0
  345. package/i18n/locales/pt/duxt/toc.json +7 -0
  346. package/i18n/locales/pt/duxt/version.json +25 -0
  347. package/i18n/locales/pt-BR/duxt/error.json +8 -0
  348. package/i18n/locales/pt-BR/duxt/nav.json +10 -0
  349. package/i18n/locales/pt-BR/duxt/page.json +10 -0
  350. package/i18n/locales/pt-BR/duxt/search.json +11 -0
  351. package/i18n/locales/pt-BR/duxt/version.json +13 -0
  352. package/mdc.config.ts +28 -0
  353. package/modules/config.ts +440 -0
  354. package/modules/devtools.ts +126 -0
  355. package/modules/git-meta.ts +349 -0
  356. package/modules/redirects.ts +152 -0
  357. package/modules/validate.ts +137 -0
  358. package/nuxt.config.ts +530 -0
  359. package/openapi-model.ts +358 -0
  360. package/openapi-parse.ts +1052 -0
  361. package/package.json +161 -0
  362. package/public/devtools/cache.html +318 -0
  363. package/public/devtools/checks.html +309 -0
  364. package/public/devtools/config.html +461 -0
  365. package/public/devtools/i18n.html +309 -0
  366. package/public/devtools/pages.html +348 -0
  367. package/public/devtools/paths.html +315 -0
  368. package/public/devtools/redirects.html +309 -0
  369. package/public/devtools/search.html +324 -0
  370. package/public/devtools/sources.html +309 -0
  371. package/public/devtools/versions.html +318 -0
  372. package/report.ts +303 -0
  373. package/repository-root.ts +26 -0
  374. package/section-reports.ts +55 -0
  375. package/sections-changelog.ts +644 -0
  376. package/sections-openapi.ts +450 -0
  377. package/sections-resolve.ts +711 -0
  378. package/sections.ts +157 -0
  379. package/server/devtools/content.ts +120 -0
  380. package/server/devtools/context.ts +38 -0
  381. package/server/devtools/entry-path.ts +30 -0
  382. package/server/devtools/handler.ts +104 -0
  383. package/server/devtools/preview.ts +461 -0
  384. package/server/devtools/render/content.ts +233 -0
  385. package/server/devtools/render/sources.ts +414 -0
  386. package/server/devtools/render/system.ts +352 -0
  387. package/server/devtools/shell.ts +611 -0
  388. package/server/devtools/sources.ts +122 -0
  389. package/server/devtools/system.ts +178 -0
  390. package/server/mcp/tools/list-pages.ts +52 -0
  391. package/server/mcp/tools/read-page.ts +57 -0
  392. package/server/mcp/tools/search-docs.ts +74 -0
  393. package/server/middleware/raw-markdown.ts +86 -0
  394. package/server/routes/llms-full.txt.get.ts +75 -0
  395. package/server/routes/llms.txt.get.ts +63 -0
  396. package/server/routes/rss.xml.get.ts +125 -0
  397. package/server/utils/duxt-server-text.ts +46 -0
  398. package/sources-git.ts +113 -0
  399. package/sources-resolve.ts +908 -0
  400. package/sources.ts +287 -0
  401. package/validate-report.ts +494 -0
@@ -0,0 +1,315 @@
1
+ <!doctype html>
2
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
3
+ <title>duxt — Paths</title><style>
4
+ :root {
5
+ color-scheme: light dark;
6
+ --bg: #fff;
7
+ --fg: #12141a;
8
+ --muted: #6b7280;
9
+ --faint: #9ca3af;
10
+ --line: #e8e8ea;
11
+ --raised: #f6f6f7;
12
+ --accent: #00a86b;
13
+ --accent-soft: #e6f7f0;
14
+ --warn: #b45309;
15
+ --error: #b91c1c;
16
+ --ok: #15803d;
17
+ --radius: 7px;
18
+ }
19
+ @media (prefers-color-scheme: dark) {
20
+ :root:not([data-theme='light']) {
21
+ --bg: #0f1115;
22
+ --fg: #e8e8ea;
23
+ --muted: #9099a8;
24
+ --faint: #5b6472;
25
+ --line: #23262d;
26
+ --raised: #171a20;
27
+ --accent: #00dc82;
28
+ --accent-soft: #10291f;
29
+ --warn: #f59e0b;
30
+ --error: #f87171;
31
+ --ok: #4ade80;
32
+ }
33
+ }
34
+ :root[data-theme='dark'] {
35
+ --bg: #0f1115;
36
+ --fg: #e8e8ea;
37
+ --muted: #9099a8;
38
+ --faint: #5b6472;
39
+ --line: #23262d;
40
+ --raised: #171a20;
41
+ --accent: #00dc82;
42
+ --accent-soft: #10291f;
43
+ --warn: #f59e0b;
44
+ --error: #f87171;
45
+ --ok: #4ade80;
46
+ }
47
+
48
+ * { box-sizing: border-box; }
49
+ body {
50
+ margin: 0;
51
+ padding: 0 0 40px;
52
+ font: 13px/1.55 ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
53
+ color: var(--fg);
54
+ background: var(--bg);
55
+ -webkit-font-smoothing: antialiased;
56
+ }
57
+ a { color: inherit; }
58
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
59
+
60
+ header.bar {
61
+ position: sticky; top: 0; z-index: 2;
62
+ background: color-mix(in srgb, var(--bg) 88%, transparent);
63
+ backdrop-filter: blur(8px);
64
+ border-bottom: 1px solid var(--line);
65
+ padding: 10px 16px 0;
66
+ }
67
+ nav { display: flex; gap: 1px; flex-wrap: wrap; }
68
+ nav a {
69
+ padding: 5px 10px; border-radius: var(--radius) var(--radius) 0 0;
70
+ text-decoration: none; color: var(--muted); font-weight: 500;
71
+ border-bottom: 2px solid transparent; line-height: 1.4;
72
+ }
73
+ nav a:hover { color: var(--fg); background: var(--raised); }
74
+ nav a[aria-current] { color: var(--fg); border-bottom-color: var(--accent); font-weight: 600; }
75
+
76
+ main { padding: 18px 16px 0; max-width: 1400px; }
77
+ h1 { margin: 0 0 3px; font-size: 15px; font-weight: 650; letter-spacing: -0.01em; }
78
+ h2 {
79
+ margin: 26px 0 8px; font-size: 12px; font-weight: 600;
80
+ text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
81
+ }
82
+ p.hint { margin: 0 0 16px; color: var(--muted); max-width: 78ch; }
83
+
84
+ .stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
85
+ .stat {
86
+ display: flex; flex-direction: column; gap: 1px;
87
+ padding: 7px 12px; min-width: 78px;
88
+ border: 1px solid var(--line); border-radius: var(--radius); background: var(--raised);
89
+ }
90
+ .stat b { font-size: 16px; font-weight: 650; font-variant-numeric: tabular-nums; line-height: 1.2; }
91
+ .stat span { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
92
+ .stat.ok b { color: var(--ok); }
93
+ .stat.warn b { color: var(--warn); }
94
+ .stat.error b { color: var(--error); }
95
+
96
+ .scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 10px; }
97
+ table { width: 100%; border-collapse: collapse; }
98
+ th, td { padding: 7px 11px; text-align: left; vertical-align: top; }
99
+ /* Offset zero, not the height of the tab bar. .scroll sets overflow-x, which
100
+ computes overflow-y to auto as well, so the box — not the page — is what a
101
+ sticky heading offsets against. At 41px the heading sat 41px down from the
102
+ box's top edge, leaving a blank band above it AND covering the first row:
103
+ the Redirects panel counted eight rules and showed seven. */
104
+ thead th {
105
+ position: sticky; top: 0; z-index: 1;
106
+ background: var(--raised);
107
+ font-weight: 600; color: var(--muted);
108
+ font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em;
109
+ white-space: nowrap; border-bottom: 1px solid var(--line);
110
+ }
111
+ tbody tr { border-top: 1px solid var(--line); }
112
+ tbody tr:first-child { border-top: 0; }
113
+ tbody tr:hover { background: var(--raised); }
114
+ td.empty { color: var(--muted); padding: 16px 11px; text-align: center; }
115
+
116
+ code {
117
+ font: 11.5px/1.5 ui-monospace, 'SF Mono', Menlo, monospace;
118
+ background: var(--raised); border: 1px solid var(--line);
119
+ padding: 0 4px; border-radius: 4px; white-space: nowrap;
120
+ }
121
+ a.file { text-decoration: none; border-bottom: 1px dashed var(--faint); }
122
+ a.file:hover { border-bottom-style: solid; color: var(--accent); }
123
+ .dim { color: var(--faint); }
124
+ .trunc {
125
+ display: inline-block; max-width: 34ch; overflow: hidden;
126
+ text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
127
+ }
128
+ td.wide { min-width: 32ch; }
129
+
130
+ .tag {
131
+ display: inline-block; border-radius: 999px; padding: 1px 7px;
132
+ font-size: 10px; font-weight: 600; white-space: nowrap;
133
+ background: var(--accent-soft); color: var(--accent);
134
+ border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
135
+ }
136
+ .tag.warn { background: transparent; color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
137
+ .tag.error { background: transparent; color: var(--error); border-color: color-mix(in srgb, var(--error) 40%, transparent); }
138
+ .tag.ok { background: transparent; color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
139
+ .tag.muted { background: var(--raised); color: var(--muted); border-color: var(--line); }
140
+
141
+ form { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; align-items: center; }
142
+ input[type=text], input[type=search] {
143
+ flex: 1; min-width: 240px; padding: 6px 11px;
144
+ border: 1px solid var(--line); border-radius: var(--radius);
145
+ font: inherit; background: var(--bg); color: inherit;
146
+ }
147
+ .filterbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
148
+ .filterbar .count {
149
+ color: var(--muted); font-size: 11.5px;
150
+ font-variant-numeric: tabular-nums; white-space: nowrap;
151
+ }
152
+ input.filter {
153
+ flex: 0 1 340px; min-width: 0; margin: 0; padding-left: 31px;
154
+ background: var(--bg) no-repeat 10px 50% / 14px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239099a8' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.6-3.6'/%3E%3C/svg%3E");
155
+ }
156
+ input.filter:focus { border-color: var(--accent); }
157
+ input.filter::-webkit-search-cancel-button { cursor: pointer; }
158
+ input::placeholder { color: var(--faint); }
159
+ button {
160
+ padding: 6px 14px; border: 1px solid var(--line); border-radius: var(--radius);
161
+ background: var(--raised); font: inherit; font-weight: 500; cursor: pointer; color: inherit;
162
+ }
163
+ button:hover { border-color: var(--accent); color: var(--accent); }
164
+
165
+ ol.steps { margin: 0 0 18px; padding: 0; list-style: none; counter-reset: step; }
166
+ ol.steps li {
167
+ position: relative; counter-increment: step;
168
+ padding: 8px 0 8px 30px; border-left: 2px solid var(--line); margin-left: 9px;
169
+ }
170
+ ol.steps li::before {
171
+ content: counter(step);
172
+ position: absolute; left: -10px; top: 8px;
173
+ width: 18px; height: 18px; border-radius: 999px;
174
+ background: var(--raised); border: 1px solid var(--line);
175
+ font-size: 10px; font-weight: 600; color: var(--muted);
176
+ display: flex; align-items: center; justify-content: center;
177
+ }
178
+ ol.steps li:last-child { border-left-color: transparent; }
179
+
180
+ .note {
181
+ padding: 10px 13px; border-radius: var(--radius);
182
+ background: var(--raised); border: 1px solid var(--line);
183
+ color: var(--muted); margin-bottom: 16px; max-width: 90ch;
184
+ }
185
+ .note code { background: var(--bg); }
186
+
187
+ details { margin-bottom: 10px; }
188
+ details > summary {
189
+ cursor: pointer; padding: 7px 11px; border-radius: var(--radius);
190
+ background: var(--raised); border: 1px solid var(--line);
191
+ font-weight: 600; list-style-position: inside;
192
+ }
193
+ details > summary:hover { border-color: var(--accent); }
194
+ details[open] > summary { border-radius: var(--radius) var(--radius) 0 0; margin-bottom: -1px; }
195
+ details[open] > summary + .scroll { border-radius: 0 0 var(--radius) var(--radius); }
196
+ details[open] > summary + .filterbar {
197
+ margin: 0; padding: 8px 11px;
198
+ background: var(--raised); border: 1px solid var(--line); border-top: 0;
199
+ }
200
+ details[open] > .filterbar + .scroll {
201
+ border-radius: 0 0 var(--radius) var(--radius); margin-top: -1px;
202
+ }
203
+
204
+ /* A disclosure inside a table cell is not the panel-sized one above: no bar,
205
+ no border, no weight — just a triangle in front of the preview. */
206
+ details.json { margin: 0; }
207
+ details.json > summary {
208
+ padding: 0; background: none; border: 0; border-radius: 0; font-weight: inherit;
209
+ list-style-position: outside; margin-left: 12px;
210
+ }
211
+ details.json > summary::marker { color: var(--faint); }
212
+ details.json > summary:hover { border: 0; }
213
+ details.json[open] > summary { margin-bottom: 6px; border-radius: 0; }
214
+ details.json[open] > summary .peek { display: none; }
215
+ pre.json {
216
+ margin: 0 0 2px; padding: 9px 11px; max-height: 320px; overflow: auto;
217
+ background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
218
+ font: 11.5px/1.6 ui-monospace, 'SF Mono', Menlo, monospace;
219
+ white-space: pre; tab-size: 2;
220
+ }
221
+ .j-key { color: var(--accent); }
222
+ .j-num { color: var(--warn); }
223
+ .j-word { color: var(--muted); font-style: italic; }
224
+
225
+ .matrix th:first-child, .matrix td:first-child {
226
+ position: sticky; left: 0; background: var(--bg);
227
+ border-right: 1px solid var(--line);
228
+ }
229
+ .matrix thead th:first-child { background: var(--raised); }
230
+ .matrix tbody tr:hover td:first-child { background: var(--raised); }
231
+ .matrix td.mark { text-align: center; }
232
+ .matrix td.yes { color: var(--ok); font-weight: 700; }
233
+ .matrix td.no { color: var(--faint); }
234
+ </style></head><body>
235
+
236
+ <main>
237
+ <h1>Paths</h1>
238
+ <p class="hint">Resolve a URL the way the theme does, step by step — and say which step failed.</p>
239
+ <form method="get">
240
+ <input type="text" name="path" value="/de/sdk/v1-9/guides/caching" placeholder="/workflows/v0.7.0/guides/add-a-body" autofocus>
241
+ <button type="submit">Resolve</button>
242
+ </form><ol class="steps"><li>Locale segment stripped: <code>/de/sdk/v1-9/guides/caching</code> → <code>/sdk/v1-9/guides/caching</code>, language <code>de</code>. <span class="dim">The locale translates the interface, never the content tree — but it does pick the collection.</span></li><li>Longest matching prefix is <code>/sdk</code> → collection <code>docs_de_sdk</code>, version <code>v2</code>, language <code>de</code>.</li><li>Falls back to <code>docs_sdk</code>. <span class="dim">A page is translated whole or not at all, so this is a chain of collections rather than a merge.</span></li><li><span class="tag error">stops here</span> Collection <code>docs_de_sdk</code> has no page at <code>/sdk/v1-9/guides/caching</code>.</li></ol><h2>The same page in other sources</h2><div class="scroll"><table>
243
+ <thead><tr><th scope="col">Prefix</th><th scope="col">Version</th><th scope="col">Language</th><th scope="col">Would be</th><th scope="col">State</th></tr></thead>
244
+ <tbody><tr><td><code>/sdk</code></td><td>v2</td><td>en <span class="tag">original</span></td><td><code>/sdk/v1-9/guides/caching</code></td><td><span class="tag muted">missing</span></td></tr><tr><td><code>/sdk</code></td><td>v2</td><td>de</td><td><code>/sdk/v1-9/guides/caching</code></td><td><span class="tag muted">missing</span></td></tr><tr><td><code>/sdk/v1.9</code></td><td>v1.9</td><td>en <span class="tag">original</span></td><td><code>/sdk/v1.9/v1-9/guides/caching</code></td><td><span class="tag muted">missing</span></td></tr><tr><td><code>/sdk/v1.9</code></td><td>v1.9</td><td>de</td><td><code>/sdk/v1.9/v1-9/guides/caching</code></td><td><span class="tag muted">missing</span></td></tr><tr><td><code>/sdk/releases</code></td><td>—</td><td>en <span class="tag">original</span></td><td><code>/sdk/releases/v1-9/guides/caching</code></td><td><span class="tag muted">missing</span></td></tr></tbody>
245
+ </table></div><h2>Nearest paths in docs_de_sdk</h2><div class="scroll"><table>
246
+ <thead><tr><th scope="col">Path</th></tr></thead>
247
+ <tbody><tr><td><code>/sdk/getting-started</code></td></tr><tr><td><code>/sdk/getting-started/install</code></td></tr><tr><td><code>/sdk/guides/deploying</code></td></tr><tr><td><code>/sdk/reference/client</code></td></tr><tr><td><code>/sdk/reference/errors</code></td></tr><tr><td><code>/sdk</code></td></tr></tbody>
248
+ </table></div>
249
+ </main>
250
+ <script>
251
+ document.addEventListener('input', (event) => {
252
+ const input = event.target;
253
+ if (!input.matches('[data-filter]')) return;
254
+
255
+ const term = input.value.trim().toLowerCase();
256
+ const scope = input.closest('details') ?? document;
257
+ const rows = scope.querySelectorAll('tbody tr');
258
+ let shown = 0;
259
+
260
+ for (const row of rows) {
261
+ row.hidden = Boolean(term) && !row.textContent.toLowerCase().includes(term);
262
+ if (!row.hidden) shown += 1;
263
+ }
264
+
265
+ const count = input.parentElement.querySelector('[data-filter-count]');
266
+ if (count) {
267
+ count.textContent = term
268
+ ? shown ? shown + ' of ' + rows.length : 'no matches'
269
+ : '';
270
+ }
271
+ });
272
+
273
+ // EVERY link and button, with no exception for the tab row — a preview has
274
+ // no tab row. Embedded in a documentation page, a reader who clicked one
275
+ // would land on another panel inside the frame while the prose around it went
276
+ // on explaining this one.
277
+ document.addEventListener('click', (event) => {
278
+ if (event.target.closest('a, button')) event.preventDefault();
279
+ });
280
+ document.addEventListener('submit', (event) => event.preventDefault());
281
+
282
+ // The page that framed this one owns the theme and the height: it knows
283
+ // which mode the reader picked, and only this document knows how tall it
284
+ // turned out. Two messages, one each way.
285
+ const theme = new URLSearchParams(location.search).get('theme');
286
+ if (theme) document.documentElement.dataset.theme = theme;
287
+
288
+ // Only from the frame that embedded this one, and only from its own origin.
289
+ // Both halves are needed: the origin alone would still accept a message from
290
+ // a sibling frame on the same site, and `event.source` alone would accept one
291
+ // from a parent on another origin. This document is served from `public/`, so
292
+ // the page framing it is always same-origin — a message from anywhere else is
293
+ // not a theme change, it is somebody else talking.
294
+ addEventListener('message', (event) => {
295
+ if (event.origin !== location.origin || event.source !== parent) return;
296
+
297
+ if (event.data && typeof event.data.duxtTheme === 'string') {
298
+ document.documentElement.dataset.theme = event.data.duxtTheme;
299
+ }
300
+ });
301
+
302
+ // Addressed rather than broadcast, for the same reason. `'*'` hands the
303
+ // height to whatever ends up framing this page, which on a consumer's site is
304
+ // not necessarily the page that asked for it.
305
+ const measure = () => parent.postMessage(
306
+ { duxtHeight: document.documentElement.scrollHeight },
307
+ location.origin
308
+ );
309
+
310
+ addEventListener('load', measure);
311
+ addEventListener('resize', measure);
312
+ document.addEventListener('toggle', measure, true);
313
+ document.addEventListener('input', () => setTimeout(measure, 0));
314
+ </script>
315
+ </body></html>
@@ -0,0 +1,309 @@
1
+ <!doctype html>
2
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
3
+ <title>duxt — Redirects</title><style>
4
+ :root {
5
+ color-scheme: light dark;
6
+ --bg: #fff;
7
+ --fg: #12141a;
8
+ --muted: #6b7280;
9
+ --faint: #9ca3af;
10
+ --line: #e8e8ea;
11
+ --raised: #f6f6f7;
12
+ --accent: #00a86b;
13
+ --accent-soft: #e6f7f0;
14
+ --warn: #b45309;
15
+ --error: #b91c1c;
16
+ --ok: #15803d;
17
+ --radius: 7px;
18
+ }
19
+ @media (prefers-color-scheme: dark) {
20
+ :root:not([data-theme='light']) {
21
+ --bg: #0f1115;
22
+ --fg: #e8e8ea;
23
+ --muted: #9099a8;
24
+ --faint: #5b6472;
25
+ --line: #23262d;
26
+ --raised: #171a20;
27
+ --accent: #00dc82;
28
+ --accent-soft: #10291f;
29
+ --warn: #f59e0b;
30
+ --error: #f87171;
31
+ --ok: #4ade80;
32
+ }
33
+ }
34
+ :root[data-theme='dark'] {
35
+ --bg: #0f1115;
36
+ --fg: #e8e8ea;
37
+ --muted: #9099a8;
38
+ --faint: #5b6472;
39
+ --line: #23262d;
40
+ --raised: #171a20;
41
+ --accent: #00dc82;
42
+ --accent-soft: #10291f;
43
+ --warn: #f59e0b;
44
+ --error: #f87171;
45
+ --ok: #4ade80;
46
+ }
47
+
48
+ * { box-sizing: border-box; }
49
+ body {
50
+ margin: 0;
51
+ padding: 0 0 40px;
52
+ font: 13px/1.55 ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
53
+ color: var(--fg);
54
+ background: var(--bg);
55
+ -webkit-font-smoothing: antialiased;
56
+ }
57
+ a { color: inherit; }
58
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
59
+
60
+ header.bar {
61
+ position: sticky; top: 0; z-index: 2;
62
+ background: color-mix(in srgb, var(--bg) 88%, transparent);
63
+ backdrop-filter: blur(8px);
64
+ border-bottom: 1px solid var(--line);
65
+ padding: 10px 16px 0;
66
+ }
67
+ nav { display: flex; gap: 1px; flex-wrap: wrap; }
68
+ nav a {
69
+ padding: 5px 10px; border-radius: var(--radius) var(--radius) 0 0;
70
+ text-decoration: none; color: var(--muted); font-weight: 500;
71
+ border-bottom: 2px solid transparent; line-height: 1.4;
72
+ }
73
+ nav a:hover { color: var(--fg); background: var(--raised); }
74
+ nav a[aria-current] { color: var(--fg); border-bottom-color: var(--accent); font-weight: 600; }
75
+
76
+ main { padding: 18px 16px 0; max-width: 1400px; }
77
+ h1 { margin: 0 0 3px; font-size: 15px; font-weight: 650; letter-spacing: -0.01em; }
78
+ h2 {
79
+ margin: 26px 0 8px; font-size: 12px; font-weight: 600;
80
+ text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
81
+ }
82
+ p.hint { margin: 0 0 16px; color: var(--muted); max-width: 78ch; }
83
+
84
+ .stats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
85
+ .stat {
86
+ display: flex; flex-direction: column; gap: 1px;
87
+ padding: 7px 12px; min-width: 78px;
88
+ border: 1px solid var(--line); border-radius: var(--radius); background: var(--raised);
89
+ }
90
+ .stat b { font-size: 16px; font-weight: 650; font-variant-numeric: tabular-nums; line-height: 1.2; }
91
+ .stat span { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
92
+ .stat.ok b { color: var(--ok); }
93
+ .stat.warn b { color: var(--warn); }
94
+ .stat.error b { color: var(--error); }
95
+
96
+ .scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 10px; }
97
+ table { width: 100%; border-collapse: collapse; }
98
+ th, td { padding: 7px 11px; text-align: left; vertical-align: top; }
99
+ /* Offset zero, not the height of the tab bar. .scroll sets overflow-x, which
100
+ computes overflow-y to auto as well, so the box — not the page — is what a
101
+ sticky heading offsets against. At 41px the heading sat 41px down from the
102
+ box's top edge, leaving a blank band above it AND covering the first row:
103
+ the Redirects panel counted eight rules and showed seven. */
104
+ thead th {
105
+ position: sticky; top: 0; z-index: 1;
106
+ background: var(--raised);
107
+ font-weight: 600; color: var(--muted);
108
+ font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em;
109
+ white-space: nowrap; border-bottom: 1px solid var(--line);
110
+ }
111
+ tbody tr { border-top: 1px solid var(--line); }
112
+ tbody tr:first-child { border-top: 0; }
113
+ tbody tr:hover { background: var(--raised); }
114
+ td.empty { color: var(--muted); padding: 16px 11px; text-align: center; }
115
+
116
+ code {
117
+ font: 11.5px/1.5 ui-monospace, 'SF Mono', Menlo, monospace;
118
+ background: var(--raised); border: 1px solid var(--line);
119
+ padding: 0 4px; border-radius: 4px; white-space: nowrap;
120
+ }
121
+ a.file { text-decoration: none; border-bottom: 1px dashed var(--faint); }
122
+ a.file:hover { border-bottom-style: solid; color: var(--accent); }
123
+ .dim { color: var(--faint); }
124
+ .trunc {
125
+ display: inline-block; max-width: 34ch; overflow: hidden;
126
+ text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
127
+ }
128
+ td.wide { min-width: 32ch; }
129
+
130
+ .tag {
131
+ display: inline-block; border-radius: 999px; padding: 1px 7px;
132
+ font-size: 10px; font-weight: 600; white-space: nowrap;
133
+ background: var(--accent-soft); color: var(--accent);
134
+ border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
135
+ }
136
+ .tag.warn { background: transparent; color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
137
+ .tag.error { background: transparent; color: var(--error); border-color: color-mix(in srgb, var(--error) 40%, transparent); }
138
+ .tag.ok { background: transparent; color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
139
+ .tag.muted { background: var(--raised); color: var(--muted); border-color: var(--line); }
140
+
141
+ form { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; align-items: center; }
142
+ input[type=text], input[type=search] {
143
+ flex: 1; min-width: 240px; padding: 6px 11px;
144
+ border: 1px solid var(--line); border-radius: var(--radius);
145
+ font: inherit; background: var(--bg); color: inherit;
146
+ }
147
+ .filterbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
148
+ .filterbar .count {
149
+ color: var(--muted); font-size: 11.5px;
150
+ font-variant-numeric: tabular-nums; white-space: nowrap;
151
+ }
152
+ input.filter {
153
+ flex: 0 1 340px; min-width: 0; margin: 0; padding-left: 31px;
154
+ background: var(--bg) no-repeat 10px 50% / 14px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239099a8' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.6-3.6'/%3E%3C/svg%3E");
155
+ }
156
+ input.filter:focus { border-color: var(--accent); }
157
+ input.filter::-webkit-search-cancel-button { cursor: pointer; }
158
+ input::placeholder { color: var(--faint); }
159
+ button {
160
+ padding: 6px 14px; border: 1px solid var(--line); border-radius: var(--radius);
161
+ background: var(--raised); font: inherit; font-weight: 500; cursor: pointer; color: inherit;
162
+ }
163
+ button:hover { border-color: var(--accent); color: var(--accent); }
164
+
165
+ ol.steps { margin: 0 0 18px; padding: 0; list-style: none; counter-reset: step; }
166
+ ol.steps li {
167
+ position: relative; counter-increment: step;
168
+ padding: 8px 0 8px 30px; border-left: 2px solid var(--line); margin-left: 9px;
169
+ }
170
+ ol.steps li::before {
171
+ content: counter(step);
172
+ position: absolute; left: -10px; top: 8px;
173
+ width: 18px; height: 18px; border-radius: 999px;
174
+ background: var(--raised); border: 1px solid var(--line);
175
+ font-size: 10px; font-weight: 600; color: var(--muted);
176
+ display: flex; align-items: center; justify-content: center;
177
+ }
178
+ ol.steps li:last-child { border-left-color: transparent; }
179
+
180
+ .note {
181
+ padding: 10px 13px; border-radius: var(--radius);
182
+ background: var(--raised); border: 1px solid var(--line);
183
+ color: var(--muted); margin-bottom: 16px; max-width: 90ch;
184
+ }
185
+ .note code { background: var(--bg); }
186
+
187
+ details { margin-bottom: 10px; }
188
+ details > summary {
189
+ cursor: pointer; padding: 7px 11px; border-radius: var(--radius);
190
+ background: var(--raised); border: 1px solid var(--line);
191
+ font-weight: 600; list-style-position: inside;
192
+ }
193
+ details > summary:hover { border-color: var(--accent); }
194
+ details[open] > summary { border-radius: var(--radius) var(--radius) 0 0; margin-bottom: -1px; }
195
+ details[open] > summary + .scroll { border-radius: 0 0 var(--radius) var(--radius); }
196
+ details[open] > summary + .filterbar {
197
+ margin: 0; padding: 8px 11px;
198
+ background: var(--raised); border: 1px solid var(--line); border-top: 0;
199
+ }
200
+ details[open] > .filterbar + .scroll {
201
+ border-radius: 0 0 var(--radius) var(--radius); margin-top: -1px;
202
+ }
203
+
204
+ /* A disclosure inside a table cell is not the panel-sized one above: no bar,
205
+ no border, no weight — just a triangle in front of the preview. */
206
+ details.json { margin: 0; }
207
+ details.json > summary {
208
+ padding: 0; background: none; border: 0; border-radius: 0; font-weight: inherit;
209
+ list-style-position: outside; margin-left: 12px;
210
+ }
211
+ details.json > summary::marker { color: var(--faint); }
212
+ details.json > summary:hover { border: 0; }
213
+ details.json[open] > summary { margin-bottom: 6px; border-radius: 0; }
214
+ details.json[open] > summary .peek { display: none; }
215
+ pre.json {
216
+ margin: 0 0 2px; padding: 9px 11px; max-height: 320px; overflow: auto;
217
+ background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
218
+ font: 11.5px/1.6 ui-monospace, 'SF Mono', Menlo, monospace;
219
+ white-space: pre; tab-size: 2;
220
+ }
221
+ .j-key { color: var(--accent); }
222
+ .j-num { color: var(--warn); }
223
+ .j-word { color: var(--muted); font-style: italic; }
224
+
225
+ .matrix th:first-child, .matrix td:first-child {
226
+ position: sticky; left: 0; background: var(--bg);
227
+ border-right: 1px solid var(--line);
228
+ }
229
+ .matrix thead th:first-child { background: var(--raised); }
230
+ .matrix tbody tr:hover td:first-child { background: var(--raised); }
231
+ .matrix td.mark { text-align: center; }
232
+ .matrix td.yes { color: var(--ok); font-weight: 700; }
233
+ .matrix td.no { color: var(--faint); }
234
+ </style></head><body>
235
+
236
+ <main>
237
+ <h1>Redirects</h1>
238
+ <p class="hint">Every redirect the server ships, with the rules <code>redirectFrom</code> generated marked as the layer’s own.</p>
239
+ <div class="stats"><div class="stat "><b>4</b><span>rules</span></div><div class="stat ok"><b>3</b><span>from redirectFrom</span></div><div class="stat "><b>1</b><span>from elsewhere</span></div></div><div class="scroll"><table>
240
+ <thead><tr><th scope="col">From</th><th scope="col">To</th><th scope="col">Status</th><th scope="col">Written by</th></tr></thead>
241
+ <tbody><tr><td><code>/de/sdk/guides/deploy</code></td><td><code>/de/sdk/guides/deploying</code></td><td>301</td><td><span class="tag ok">redirectFrom</span></td></tr><tr><td><code>/sdk/guides/deploy</code></td><td><code>/sdk/guides/deploying</code></td><td>301</td><td><span class="tag ok">redirectFrom</span></td></tr><tr><td><code>/sdk/v1-9/guides/deploy</code></td><td><code>/sdk/v1-9/guides/deploying</code></td><td>301</td><td><span class="tag ok">redirectFrom</span></td></tr><tr><td><code>/sitemap.xml</code></td><td><code>/sitemap_index.xml</code></td><td>307</td><td><span class="tag muted">another module</span></td></tr></tbody>
242
+ </table></div><div class="note">One rule per prefix a page is served under — the repository segment, the version segment and every locale — because an old URL was bookmarked under exactly one of them and nothing says which.</div>
243
+ </main>
244
+ <script>
245
+ document.addEventListener('input', (event) => {
246
+ const input = event.target;
247
+ if (!input.matches('[data-filter]')) return;
248
+
249
+ const term = input.value.trim().toLowerCase();
250
+ const scope = input.closest('details') ?? document;
251
+ const rows = scope.querySelectorAll('tbody tr');
252
+ let shown = 0;
253
+
254
+ for (const row of rows) {
255
+ row.hidden = Boolean(term) && !row.textContent.toLowerCase().includes(term);
256
+ if (!row.hidden) shown += 1;
257
+ }
258
+
259
+ const count = input.parentElement.querySelector('[data-filter-count]');
260
+ if (count) {
261
+ count.textContent = term
262
+ ? shown ? shown + ' of ' + rows.length : 'no matches'
263
+ : '';
264
+ }
265
+ });
266
+
267
+ // EVERY link and button, with no exception for the tab row — a preview has
268
+ // no tab row. Embedded in a documentation page, a reader who clicked one
269
+ // would land on another panel inside the frame while the prose around it went
270
+ // on explaining this one.
271
+ document.addEventListener('click', (event) => {
272
+ if (event.target.closest('a, button')) event.preventDefault();
273
+ });
274
+ document.addEventListener('submit', (event) => event.preventDefault());
275
+
276
+ // The page that framed this one owns the theme and the height: it knows
277
+ // which mode the reader picked, and only this document knows how tall it
278
+ // turned out. Two messages, one each way.
279
+ const theme = new URLSearchParams(location.search).get('theme');
280
+ if (theme) document.documentElement.dataset.theme = theme;
281
+
282
+ // Only from the frame that embedded this one, and only from its own origin.
283
+ // Both halves are needed: the origin alone would still accept a message from
284
+ // a sibling frame on the same site, and `event.source` alone would accept one
285
+ // from a parent on another origin. This document is served from `public/`, so
286
+ // the page framing it is always same-origin — a message from anywhere else is
287
+ // not a theme change, it is somebody else talking.
288
+ addEventListener('message', (event) => {
289
+ if (event.origin !== location.origin || event.source !== parent) return;
290
+
291
+ if (event.data && typeof event.data.duxtTheme === 'string') {
292
+ document.documentElement.dataset.theme = event.data.duxtTheme;
293
+ }
294
+ });
295
+
296
+ // Addressed rather than broadcast, for the same reason. `'*'` hands the
297
+ // height to whatever ends up framing this page, which on a consumer's site is
298
+ // not necessarily the page that asked for it.
299
+ const measure = () => parent.postMessage(
300
+ { duxtHeight: document.documentElement.scrollHeight },
301
+ location.origin
302
+ );
303
+
304
+ addEventListener('load', measure);
305
+ addEventListener('resize', measure);
306
+ document.addEventListener('toggle', measure, true);
307
+ document.addEventListener('input', () => setTimeout(measure, 0));
308
+ </script>
309
+ </body></html>