@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,271 @@
1
+ <script setup lang="ts">
2
+ // Two roots — the trigger and the dialog — so Vue cannot decide which one
3
+ // inherits a class from the parent and drops it with a warning. The trigger
4
+ // takes them explicitly.
5
+ defineOptions({ inheritAttrs: false });
6
+
7
+ import type { DuxtSearchHit } from '@duxt/composables/useDuxtSearch';
8
+
9
+ // Full-text search over EVERY source, not just the one being read. Content
10
+ // builds the index at build time and queries it with SQLite's FTS, so the
11
+ // ranking is the database's rather than a substring match of ours — and there
12
+ // is no search service to run. The merge across sources, and why the results
13
+ // are ranked together rather than grouped per repository, is in
14
+ // `useDuxtSearch`.
15
+ //
16
+ // The index is fetched on first open, not shipped with every page: a docs site
17
+ // should not pay for search on a page nobody searches from.
18
+ const open = ref(false);
19
+ const query = ref('');
20
+ const router = useRouter();
21
+ const localeLink = useDuxtLink();
22
+
23
+ const { search, init, labelled } = useDuxtSearch();
24
+
25
+ const results = ref<DuxtSearchHit[]>([]);
26
+
27
+ /** True while the list shows near-misses rather than actual matches. */
28
+ const approximate = ref(false);
29
+
30
+ let pending: ReturnType<typeof setTimeout> | undefined;
31
+
32
+ // Two awaits per keystroke, so a slow answer could land after a newer one and
33
+ // overwrite it. Each run takes a number and drops its result if the term moved
34
+ // on in the meantime.
35
+ let run = 0;
36
+
37
+ // Debounced by hand rather than through VueUse: one timer is not worth another
38
+ // dependency in a layer a consumer installs.
39
+ watch(query, (term) => {
40
+ clearTimeout(pending);
41
+ pending = setTimeout(async () => {
42
+ const current = ++run;
43
+
44
+ const settle = (hits: DuxtSearchHit[], fuzzy: boolean) => {
45
+ if (current !== run) return;
46
+ results.value = hits;
47
+ approximate.value = fuzzy;
48
+ };
49
+
50
+ if (!term.trim()) return settle([], false);
51
+
52
+ const found = await search(term, 20);
53
+ settle(found.hits, found.approximate);
54
+ }, 120);
55
+ });
56
+
57
+ const duxt = useDuxtConfig();
58
+ const { recent, load } = useRecentPages();
59
+
60
+ /** Which section a path belongs to, for grouping the hits. */
61
+ function sectionOf(path: string) {
62
+ return (
63
+ duxt.sections?.find((section) => section.to && path.startsWith(section.to))
64
+ ?.label ?? 'Documentation'
65
+ );
66
+ }
67
+
68
+ /**
69
+ * Hits grouped by section rather than by page. Grouping by page produced one
70
+ * heading per result; a section groups many, which is what a heading is for —
71
+ * and the reader already thinks in sections, because the navbar shows them.
72
+ */
73
+ const grouped = computed(() => {
74
+ // With several sources the list is ONE ranked list with a badge per hit —
75
+ // grouping it by section would re-sort exactly the ranking the merge just
76
+ // produced. See `useDuxtSearch` for why ranking beats grouping here.
77
+ if (labelled.value) {
78
+ return [{ label: '', hits: results.value }];
79
+ }
80
+
81
+ const bySection = new Map<string, DuxtSearchHit[]>();
82
+
83
+ for (const hit of results.value) {
84
+ // A section label may be configured per locale; the group key has to be a
85
+ // plain string, and by this point useDuxtConfig has resolved it.
86
+ const label = asText(sectionOf(hit.id)) ?? 'Documentation';
87
+ bySection.set(label, [...(bySection.get(label) ?? []), hit]);
88
+ }
89
+
90
+ return [...bySection.entries()].map(([label, hits]) => ({ label, hits }));
91
+ });
92
+
93
+ async function show() {
94
+ load();
95
+ open.value = true;
96
+ await init();
97
+ }
98
+
99
+ function go(id: string) {
100
+ open.value = false;
101
+ query.value = '';
102
+ results.value = [];
103
+ approximate.value = false;
104
+ router.push(localeLink(id)!);
105
+ }
106
+
107
+ /** Where a hit sits: the page, and the headings above it inside that page. */
108
+ function context(result: DuxtSearchSection) {
109
+ const page = result.titles[0];
110
+ const between = result.titles.slice(1);
111
+
112
+ return [page, ...between].filter(Boolean).join(' › ');
113
+ }
114
+
115
+ onMounted(() => {
116
+ const handler = (event: KeyboardEvent) => {
117
+ if (event.key.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey)) {
118
+ event.preventDefault();
119
+
120
+ if (open.value) {
121
+ open.value = false;
122
+ } else {
123
+ void show();
124
+ }
125
+ }
126
+ };
127
+
128
+ window.addEventListener('keydown', handler);
129
+ onBeforeUnmount(() => window.removeEventListener('keydown', handler));
130
+ });
131
+ </script>
132
+
133
+ <template>
134
+ <UiButton
135
+ v-bind="$attrs"
136
+ variant="outline"
137
+ size="sm"
138
+ class="w-full justify-start gap-2 text-muted-foreground"
139
+ @click="show"
140
+ >
141
+ <Icon name="lucide:search" class="size-4" />
142
+ <span class="text-sm">{{ $t('duxt.search.label') }}</span>
143
+ <kbd
144
+ class="ml-auto hidden rounded border bg-muted px-1.5 font-mono text-[10px] sm:inline-block"
145
+ >
146
+ ⌘K
147
+ </kbd>
148
+ </UiButton>
149
+
150
+ <UiCommandDialog v-model:open="open">
151
+ <!-- The input is ours, not CommandInput: that one writes into Command's own
152
+ filterState, and Command would then score the list a second time
153
+ against rendered text — dropping entries that mounted after the term
154
+ changed. Leaving its state empty keeps one filter in charge, the
155
+ database's. It also means CommandEmpty never renders, so the empty
156
+ state is ours too.
157
+
158
+ No clear button: DialogContent already draws a close X, and two of
159
+ them in the same corner is one too many. -->
160
+ <div class="flex items-center gap-2 border-b px-3">
161
+ <Icon
162
+ name="lucide:search"
163
+ class="size-4 shrink-0 text-muted-foreground"
164
+ />
165
+ <input
166
+ v-model="query"
167
+ class="flex h-11 w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground"
168
+ :placeholder="$t('duxt.search.placeholder')"
169
+ autofocus
170
+ />
171
+ </div>
172
+
173
+ <UiCommandList class="max-h-[60vh]">
174
+ <!-- An empty box is a dead end. Without a term the dialog offers the
175
+ sections as entry points and, once there is any history, the pages
176
+ this reader came from — which is what they are most likely after. -->
177
+ <template v-if="!query.trim()">
178
+ <UiCommandGroup
179
+ v-if="recent.length"
180
+ :heading="$t('duxt.search.recent')"
181
+ >
182
+ <UiCommandItem
183
+ v-for="page in recent"
184
+ :key="page.path"
185
+ :value="`recent ${page.path}`"
186
+ class="gap-2"
187
+ @select="go(page.path)"
188
+ >
189
+ <Icon
190
+ name="lucide:history"
191
+ class="size-3.5 shrink-0 text-muted-foreground"
192
+ />
193
+ <span class="truncate">{{ page.title }}</span>
194
+ <span class="ml-auto truncate pl-3 text-xs text-muted-foreground">
195
+ {{ sectionOf(page.path) }}
196
+ </span>
197
+ </UiCommandItem>
198
+ </UiCommandGroup>
199
+
200
+ <UiCommandGroup :heading="$t('duxt.search.sections')">
201
+ <UiCommandItem
202
+ v-for="section in duxt.sections ?? []"
203
+ :key="section.to"
204
+ :value="`section ${section.label}`"
205
+ class="gap-2"
206
+ @select="go(section.to ?? '/')"
207
+ >
208
+ <Icon
209
+ :name="section.icon ?? 'lucide:book-open'"
210
+ class="size-3.5 shrink-0 text-muted-foreground"
211
+ />
212
+ <span class="truncate">{{ section.label }}</span>
213
+ </UiCommandItem>
214
+ </UiCommandGroup>
215
+ </template>
216
+
217
+ <div
218
+ v-else-if="!results.length"
219
+ class="flex flex-col items-center gap-2 py-12 text-sm text-muted-foreground"
220
+ >
221
+ <Icon name="lucide:search-x" class="size-5 opacity-60" />
222
+ {{ $t('duxt.search.empty', { query }) }}
223
+ </div>
224
+
225
+ <!-- Say so when the exact search came up empty, otherwise a near-miss
226
+ reads as a match and the reader wonders why their term is missing
227
+ from the result. -->
228
+ <div
229
+ v-if="approximate"
230
+ class="flex items-center gap-2 px-3 pt-3 pb-1 text-xs text-muted-foreground"
231
+ >
232
+ <Icon name="lucide:sparkles" class="size-3.5 shrink-0" />
233
+ {{ $t('duxt.search.approximate', { query }) }}
234
+ </div>
235
+
236
+ <!-- One line per hit, grouped by section: the page is context on the
237
+ right, not a heading of its own. -->
238
+ <UiCommandGroup
239
+ v-for="group in grouped"
240
+ :key="group.label"
241
+ :heading="group.label || undefined"
242
+ >
243
+ <UiCommandItem
244
+ v-for="hit in group.hits"
245
+ :key="hit.id"
246
+ :value="hit.id"
247
+ class="gap-2"
248
+ @select="go(hit.id)"
249
+ >
250
+ <Icon
251
+ :name="hit.level > 1 ? 'lucide:hash' : 'lucide:file-text'"
252
+ class="size-3.5 shrink-0 text-muted-foreground"
253
+ />
254
+ <span class="truncate">{{ hit.title }}</span>
255
+ <span class="ml-auto truncate pl-3 text-xs text-muted-foreground">
256
+ {{ context(hit) }}
257
+ </span>
258
+ <!-- Which repository and version this came out of. Only drawn where
259
+ there is more than one, so a single-source site sees nothing. -->
260
+ <UiBadge
261
+ v-if="hit.source"
262
+ variant="secondary"
263
+ class="shrink-0 font-mono text-[10px]"
264
+ >
265
+ {{ hit.source.label }}
266
+ </UiBadge>
267
+ </UiCommandItem>
268
+ </UiCommandGroup>
269
+ </UiCommandList>
270
+ </UiCommandDialog>
271
+ </template>
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ // The second navbar row: top-level sections of the documentation, each an
3
+ // entry point into a part of the tree. nuxt.com's docs read this way, and it
4
+ // keeps the sidebar showing one section instead of everything at once.
5
+ const path = useDuxtPath();
6
+ const localeLink = useDuxtLink();
7
+
8
+ // Whether the row shows at all is shared with the layouts, which offset their
9
+ // sticky columns by the header this row is part of — see `useDuxtSectionRow`.
10
+ const { sections, visible } = useDuxtSectionRow();
11
+
12
+ /**
13
+ * ONE entry is lit, never two — see `currentSection`. An area whose parts nest
14
+ * (`/demo` and `/demo/api`) had both chips marked, and a row that says the
15
+ * reader is in two places at once says nothing.
16
+ */
17
+ const active = computed(() => currentSection(sections.value, path.value));
18
+
19
+ function isActive(to?: string) {
20
+ return Boolean(to && to === active.value?.to);
21
+ }
22
+
23
+ /**
24
+ * A section is rarely the page itself — it is the branch the page sits in. So
25
+ * the exact match announces `page` and an ancestor announces `true`, which is
26
+ * the distinction `aria-current` exists to make.
27
+ */
28
+ function current(to?: string) {
29
+ if (!isActive(to)) return undefined;
30
+ return path.value === to ? 'page' : 'true';
31
+ }
32
+ </script>
33
+
34
+ <template>
35
+ <!-- Hidden below lg: the mobile sheet lists the same sections, and a row
36
+ that scrolls sideways under the header is worse than no row. -->
37
+ <!-- `top-14` is one pixel short: the header is h-14 PLUS its own border-b,
38
+ so the row parked over that border and the two swapped places by a pixel
39
+ as the browser rounded the scroll offset. -->
40
+ <div
41
+ v-if="visible"
42
+ class="sticky top-[calc(3.5rem_+_1px)] z-40 hidden border-b bg-background/95 backdrop-blur-md lg:block"
43
+ >
44
+ <nav
45
+ class="mx-auto flex max-w-[90rem] items-center gap-1 overflow-x-auto px-4 lg:px-8"
46
+ :aria-label="$t('duxt.nav.sections')"
47
+ >
48
+ <NuxtLink
49
+ v-for="section in sections"
50
+ :key="section.to"
51
+ :to="localeLink(section.to)"
52
+ :aria-current="current(section.to)"
53
+ class="my-1.5 flex shrink-0 items-center gap-2 rounded-md px-3 py-1.5 text-sm transition-colors"
54
+ :class="
55
+ isActive(section.to)
56
+ ? 'bg-accent font-medium text-foreground'
57
+ : 'text-muted-foreground hover:bg-accent/60 hover:text-foreground'
58
+ "
59
+ >
60
+ <Icon v-if="section.icon" :name="section.icon" class="size-4" />
61
+ {{ section.label }}
62
+ </NuxtLink>
63
+ </nav>
64
+ </div>
65
+ </template>
@@ -0,0 +1,46 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * The shortcut sheet, on `?`.
4
+ *
5
+ * Its list is `duxtShortcuts`, the same array the handlers match against, so
6
+ * the sheet cannot come to advertise a key nothing binds.
7
+ */
8
+ const open = ref(false);
9
+
10
+ onDuxtShortcut(
11
+ (event) => event.key === '?' && !event.metaKey && !event.ctrlKey,
12
+ () => (open.value = !open.value)
13
+ );
14
+ </script>
15
+
16
+ <template>
17
+ <UiDialog v-model:open="open">
18
+ <UiDialogContent class="sm:max-w-sm">
19
+ <UiDialogHeader>
20
+ <UiDialogTitle>{{ $t('duxt.shortcuts.title') }}</UiDialogTitle>
21
+ <UiDialogDescription class="sr-only">
22
+ {{ $t('duxt.shortcuts.title') }}
23
+ </UiDialogDescription>
24
+ </UiDialogHeader>
25
+
26
+ <ul class="flex flex-col gap-2 text-sm">
27
+ <li
28
+ v-for="shortcut in duxtShortcuts"
29
+ :key="shortcut.label"
30
+ class="flex items-center justify-between gap-4"
31
+ >
32
+ <span>{{ $t(shortcut.label) }}</span>
33
+ <span class="flex gap-1">
34
+ <kbd
35
+ v-for="key in shortcut.keys"
36
+ :key="key"
37
+ class="rounded border bg-muted px-1.5 py-0.5 font-mono text-xs"
38
+ >
39
+ {{ key }}
40
+ </kbd>
41
+ </span>
42
+ </li>
43
+ </ul>
44
+ </UiDialogContent>
45
+ </UiDialog>
46
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * The first focusable element on the page.
4
+ *
5
+ * Without it every page change means tabbing through the header, the section
6
+ * row and the whole sidebar before the first word of the text. Invisible until
7
+ * focused — `sr-only` collapses it to a single pixel, and the focus state puts
8
+ * it back on screen above everything else.
9
+ *
10
+ * The click is handled rather than left to the hash, because the browser only
11
+ * moves FOCUS to a fragment target that can hold it. Moving it here keeps the
12
+ * URL clean and works whether or not `<main>` carries a tabindex.
13
+ */
14
+ const target = '#duxt-main';
15
+
16
+ function jump(event: MouseEvent) {
17
+ const main = document.querySelector<HTMLElement>(target);
18
+ if (!main) return;
19
+
20
+ event.preventDefault();
21
+ main.focus();
22
+ main.scrollIntoView();
23
+ }
24
+ </script>
25
+
26
+ <template>
27
+ <a
28
+ :href="target"
29
+ class="sr-only rounded-md bg-background px-4 py-2 text-sm font-medium ring-2 ring-ring focus:not-sr-only focus:fixed focus:top-3 focus:left-3 focus:z-100"
30
+ @click="jump"
31
+ >
32
+ {{ $t('duxt.nav.skip') }}
33
+ </a>
34
+ </template>
@@ -0,0 +1,86 @@
1
+ <script setup lang="ts">
2
+ interface TocLink {
3
+ id: string;
4
+ text: string;
5
+ depth: number;
6
+ children?: TocLink[];
7
+ }
8
+
9
+ const props = defineProps<{ links: TocLink[] }>();
10
+
11
+ const duxt = useDuxtConfig();
12
+ const localeLink = useDuxtLink();
13
+
14
+ // Flat list of every id, in document order, for the observer to watch.
15
+ const ids = computed(() =>
16
+ props.links.flatMap((link) => [
17
+ link.id,
18
+ ...(link.children ?? []).map((child) => child.id)
19
+ ])
20
+ );
21
+
22
+ const active = useActiveHeading(ids);
23
+ </script>
24
+
25
+ <template>
26
+ <div class="space-y-8 text-sm">
27
+ <nav v-if="links?.length" :aria-label="$t('duxt.toc.title')">
28
+ <p class="mb-3 font-medium">{{ $t('duxt.toc.title') }}</p>
29
+ <ul class="space-y-1 border-l">
30
+ <li v-for="link in links" :key="link.id">
31
+ <a
32
+ :href="`#${link.id}`"
33
+ class="-ml-px block border-l py-1 pl-4 transition-colors"
34
+ :class="
35
+ active === link.id
36
+ ? 'border-primary text-foreground'
37
+ : 'border-transparent text-muted-foreground hover:border-primary/60 hover:text-foreground'
38
+ "
39
+ >
40
+ {{ link.text }}
41
+ </a>
42
+ <ul v-if="link.children?.length">
43
+ <li v-for="child in link.children" :key="child.id">
44
+ <a
45
+ :href="`#${child.id}`"
46
+ class="-ml-px block border-l py-1 pl-7 transition-colors"
47
+ :class="
48
+ active === child.id
49
+ ? 'border-primary text-foreground'
50
+ : 'border-transparent text-muted-foreground hover:border-primary/60 hover:text-foreground'
51
+ "
52
+ >
53
+ {{ child.text }}
54
+ </a>
55
+ </li>
56
+ </ul>
57
+ </li>
58
+ </ul>
59
+ </nav>
60
+
61
+ <!-- The fixed link block that belongs on every page, configured once. -->
62
+ <nav
63
+ v-if="duxt.aside?.links?.length"
64
+ :aria-label="duxt.aside.title ?? 'Community'"
65
+ >
66
+ <p class="mb-3 font-medium">{{ duxt.aside.title ?? 'Community' }}</p>
67
+ <ul class="space-y-2">
68
+ <li v-for="link in duxt.aside.links" :key="link.to">
69
+ <NuxtLink
70
+ :to="localeLink(link.to)"
71
+ :target="link.external ? '_blank' : undefined"
72
+ class="flex items-center gap-2 text-muted-foreground transition-colors hover:text-foreground"
73
+ >
74
+ <Icon v-if="link.icon" :name="link.icon" class="size-4" />
75
+ {{ link.label }}
76
+ <Icon
77
+ v-if="link.external"
78
+ name="lucide:arrow-up-right"
79
+ class="size-3 opacity-50"
80
+ />
81
+ </NuxtLink>
82
+ </li>
83
+ </ul>
84
+ </nav>
85
+ </div>
86
+ </template>
@@ -0,0 +1,64 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * The visible half of a missing translation.
4
+ *
5
+ * `noindex` and the canonical in `[...slug].vue` tell a crawler that this page
6
+ * is not a German page; they tell the reader nothing, and the reader is the one
7
+ * looking at English text on a URL that promised their language. Silently
8
+ * serving the fallback is what Docusaurus and VitePress do, and it is why a
9
+ * stale or absent translation there is invisible to everyone except the person
10
+ * reading it.
11
+ *
12
+ * A 404 would be the other option and the worse one: it punishes a reader for a
13
+ * gap the writer left.
14
+ */
15
+ const props = defineProps<{
16
+ /** The locale actually served; absent means the untranslated original. */
17
+ from?: string;
18
+ }>();
19
+
20
+ const { locales, defaultLocale } = useI18n();
21
+
22
+ /**
23
+ * The language's own name, not its code.
24
+ *
25
+ * `nuxt.config.ts` carries the endonym on every locale — each language written
26
+ * in itself — because that is what a language switcher must show, and it is
27
+ * what this sentence needs too.
28
+ */
29
+ const languageOf = (code: string | undefined) =>
30
+ locales.value.find((entry) =>
31
+ typeof entry === 'string' ? entry === code : entry.code === code
32
+ );
33
+
34
+ const shown = computed(() => {
35
+ const entry = languageOf(props.from ?? defaultLocale);
36
+ return typeof entry === 'string' ? entry : (entry?.name ?? entry?.code ?? '');
37
+ });
38
+ </script>
39
+
40
+ <template>
41
+ <!-- Same shape as DuxtVersionBanner, deliberately: a reader learns what a
42
+ bordered notice above the title means once, and a second style for a
43
+ second kind of notice is one style too many. Sky, because this is
44
+ information about the page rather than a warning about its content. -->
45
+ <UiAlert
46
+ role="status"
47
+ class="mb-6 flex items-start gap-2.5 rounded-md border border-l-2 border-l-sky-500 bg-muted/30 px-3 py-2.5"
48
+ >
49
+ <Icon
50
+ name="lucide:languages"
51
+ class="mt-[3px] size-4 shrink-0 text-sky-500"
52
+ />
53
+
54
+ <div class="min-w-0 flex-1">
55
+ <UiAlertTitle class="mb-0.5 font-medium text-foreground">
56
+ {{ $t('duxt.page.untranslated.title') }}
57
+ </UiAlertTitle>
58
+
59
+ <UiAlertDescription class="text-muted-foreground">
60
+ {{ $t('duxt.page.untranslated.description', { language: shown }) }}
61
+ </UiAlertDescription>
62
+ </div>
63
+ </UiAlert>
64
+ </template>