@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.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/app/app.config.ts +13 -0
- package/app/app.vue +122 -0
- package/app/assets/css/duxt.css +555 -0
- package/app/assets/css/typeset.css +498 -0
- package/app/components/DuxtBrand.vue +53 -0
- package/app/components/DuxtBreadcrumb.vue +37 -0
- package/app/components/DuxtCodeBlock.vue +143 -0
- package/app/components/DuxtCopyPage.vue +117 -0
- package/app/components/DuxtFooter.vue +86 -0
- package/app/components/DuxtHeader.vue +496 -0
- package/app/components/DuxtJsonEditor.vue +359 -0
- package/app/components/DuxtLandingProse.vue +90 -0
- package/app/components/DuxtLandingShowcase.vue +295 -0
- package/app/components/DuxtLiveWindow.vue +343 -0
- package/app/components/DuxtLocale.vue +86 -0
- package/app/components/DuxtMetaList.vue +76 -0
- package/app/components/DuxtNavigation.vue +94 -0
- package/app/components/DuxtNavigationLink.vue +41 -0
- package/app/components/DuxtOpenApiCallbacks.vue +92 -0
- package/app/components/DuxtOpenApiClient.vue +1339 -0
- package/app/components/DuxtOpenApiMedia.vue +108 -0
- package/app/components/DuxtOpenApiMethod.vue +59 -0
- package/app/components/DuxtOpenApiParameters.vue +114 -0
- package/app/components/DuxtOpenApiResponses.vue +109 -0
- package/app/components/DuxtOpenApiSchema.vue +365 -0
- package/app/components/DuxtOpenApiSecurity.vue +115 -0
- package/app/components/DuxtPageFeedback.vue +56 -0
- package/app/components/DuxtPageInfo.vue +194 -0
- package/app/components/DuxtPageNav.vue +93 -0
- package/app/components/DuxtPreviewSkeleton.vue +262 -0
- package/app/components/DuxtProgress.vue +45 -0
- package/app/components/DuxtSearch.vue +271 -0
- package/app/components/DuxtSections.vue +65 -0
- package/app/components/DuxtShortcuts.vue +46 -0
- package/app/components/DuxtSkipLink.vue +34 -0
- package/app/components/DuxtToc.vue +86 -0
- package/app/components/DuxtTranslationBanner.vue +64 -0
- package/app/components/DuxtVersion.vue +168 -0
- package/app/components/DuxtVersionBanner.vue +109 -0
- package/app/components/OgImage/Duxt.satori.vue +82 -0
- package/app/components/content/Accordion.vue +37 -0
- package/app/components/content/AccordionItem.vue +46 -0
- package/app/components/content/Callout.vue +58 -0
- package/app/components/content/ChangelogGroup.vue +91 -0
- package/app/components/content/ChangelogReleases.vue +520 -0
- package/app/components/content/CodeGroup.vue +183 -0
- package/app/components/content/DevtoolsPanel.vue +107 -0
- package/app/components/content/Field.vue +50 -0
- package/app/components/content/FieldGroup.vue +8 -0
- package/app/components/content/FileTree.vue +92 -0
- package/app/components/content/Mermaid.vue +70 -0
- package/app/components/content/OpenApiOperation.vue +185 -0
- package/app/components/content/OpenApiOperations.vue +72 -0
- package/app/components/content/OpenApiOverview.vue +302 -0
- package/app/components/content/PackageManagers.vue +187 -0
- package/app/components/content/PageCards.vue +48 -0
- package/app/components/content/Partial.vue +65 -0
- package/app/components/content/Preview.vue +173 -0
- package/app/components/content/ProseA.vue +55 -0
- package/app/components/content/ProseH2.vue +53 -0
- package/app/components/content/ProseH3.vue +53 -0
- package/app/components/content/ProseH4.vue +53 -0
- package/app/components/content/ProseImg.vue +87 -0
- package/app/components/content/ProsePre.vue +58 -0
- package/app/components/content/ProseTable.vue +11 -0
- package/app/components/content/Since.vue +21 -0
- package/app/components/content/Steps.vue +20 -0
- package/app/components/content/Tab.vue +23 -0
- package/app/components/content/TabGroup.vue +58 -0
- package/app/components/ui/alert/Alert.vue +21 -0
- package/app/components/ui/alert/AlertDescription.vue +22 -0
- package/app/components/ui/alert/AlertTitle.vue +22 -0
- package/app/components/ui/alert/index.ts +24 -0
- package/app/components/ui/badge/Badge.vue +28 -0
- package/app/components/ui/badge/index.ts +52 -0
- package/app/components/ui/breadcrumb/Breadcrumb.vue +13 -0
- package/app/components/ui/breadcrumb/BreadcrumbEllipsis.vue +22 -0
- package/app/components/ui/breadcrumb/BreadcrumbItem.vue +17 -0
- package/app/components/ui/breadcrumb/BreadcrumbLink.vue +24 -0
- package/app/components/ui/breadcrumb/BreadcrumbList.vue +22 -0
- package/app/components/ui/breadcrumb/BreadcrumbPage.vue +20 -0
- package/app/components/ui/breadcrumb/BreadcrumbSeparator.vue +21 -0
- package/app/components/ui/breadcrumb/index.ts +7 -0
- package/app/components/ui/button/Button.vue +31 -0
- package/app/components/ui/button/index.ts +39 -0
- package/app/components/ui/card/Card.vue +22 -0
- package/app/components/ui/card/CardAction.vue +22 -0
- package/app/components/ui/card/CardContent.vue +14 -0
- package/app/components/ui/card/CardDescription.vue +17 -0
- package/app/components/ui/card/CardFooter.vue +17 -0
- package/app/components/ui/card/CardHeader.vue +22 -0
- package/app/components/ui/card/CardTitle.vue +17 -0
- package/app/components/ui/card/index.ts +7 -0
- package/app/components/ui/collapsible/Collapsible.vue +19 -0
- package/app/components/ui/collapsible/CollapsibleContent.vue +12 -0
- package/app/components/ui/collapsible/CollapsibleTrigger.vue +12 -0
- package/app/components/ui/collapsible/index.ts +3 -0
- package/app/components/ui/command/Command.vue +98 -0
- package/app/components/ui/command/CommandDialog.vue +42 -0
- package/app/components/ui/command/CommandEmpty.vue +31 -0
- package/app/components/ui/command/CommandGroup.vue +52 -0
- package/app/components/ui/command/CommandInput.vue +45 -0
- package/app/components/ui/command/CommandItem.vue +85 -0
- package/app/components/ui/command/CommandList.vue +32 -0
- package/app/components/ui/command/CommandSeparator.vue +23 -0
- package/app/components/ui/command/CommandShortcut.vue +19 -0
- package/app/components/ui/command/index.ts +29 -0
- package/app/components/ui/dialog/Dialog.vue +15 -0
- package/app/components/ui/dialog/DialogClose.vue +12 -0
- package/app/components/ui/dialog/DialogContent.vue +61 -0
- package/app/components/ui/dialog/DialogDescription.vue +25 -0
- package/app/components/ui/dialog/DialogFooter.vue +30 -0
- package/app/components/ui/dialog/DialogHeader.vue +17 -0
- package/app/components/ui/dialog/DialogOverlay.vue +28 -0
- package/app/components/ui/dialog/DialogScrollContent.vue +65 -0
- package/app/components/ui/dialog/DialogTitle.vue +25 -0
- package/app/components/ui/dialog/DialogTrigger.vue +12 -0
- package/app/components/ui/dialog/index.ts +10 -0
- package/app/components/ui/dropdown-menu/DropdownMenu.vue +19 -0
- package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +47 -0
- package/app/components/ui/dropdown-menu/DropdownMenuContent.vue +47 -0
- package/app/components/ui/dropdown-menu/DropdownMenuGroup.vue +12 -0
- package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +41 -0
- package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +27 -0
- package/app/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +21 -0
- package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +48 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +19 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSub.vue +19 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSubContent.vue +34 -0
- package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +34 -0
- package/app/components/ui/dropdown-menu/DropdownMenuTrigger.vue +17 -0
- package/app/components/ui/dropdown-menu/index.ts +16 -0
- package/app/components/ui/input/Input.vue +35 -0
- package/app/components/ui/input/index.ts +1 -0
- package/app/components/ui/navigation-menu/NavigationMenu.vue +42 -0
- package/app/components/ui/navigation-menu/NavigationMenuContent.vue +35 -0
- package/app/components/ui/navigation-menu/NavigationMenuIndicator.vue +32 -0
- package/app/components/ui/navigation-menu/NavigationMenuItem.vue +23 -0
- package/app/components/ui/navigation-menu/NavigationMenuLink.vue +30 -0
- package/app/components/ui/navigation-menu/NavigationMenuList.vue +30 -0
- package/app/components/ui/navigation-menu/NavigationMenuTrigger.vue +31 -0
- package/app/components/ui/navigation-menu/NavigationMenuViewport.vue +30 -0
- package/app/components/ui/navigation-menu/index.ts +14 -0
- package/app/components/ui/scroll-area/ScrollArea.vue +31 -0
- package/app/components/ui/scroll-area/ScrollBar.vue +38 -0
- package/app/components/ui/scroll-area/index.ts +2 -0
- package/app/components/ui/select/Select.vue +15 -0
- package/app/components/ui/select/SelectContent.vue +59 -0
- package/app/components/ui/select/SelectGroup.vue +19 -0
- package/app/components/ui/select/SelectItem.vue +42 -0
- package/app/components/ui/select/SelectItemText.vue +12 -0
- package/app/components/ui/select/SelectLabel.vue +16 -0
- package/app/components/ui/select/SelectScrollDownButton.vue +28 -0
- package/app/components/ui/select/SelectScrollUpButton.vue +28 -0
- package/app/components/ui/select/SelectSeparator.vue +20 -0
- package/app/components/ui/select/SelectTrigger.vue +32 -0
- package/app/components/ui/select/SelectValue.vue +12 -0
- package/app/components/ui/select/index.ts +11 -0
- package/app/components/ui/separator/Separator.vue +30 -0
- package/app/components/ui/separator/index.ts +1 -0
- package/app/components/ui/sheet/Sheet.vue +15 -0
- package/app/components/ui/sheet/SheetClose.vue +12 -0
- package/app/components/ui/sheet/SheetContent.vue +68 -0
- package/app/components/ui/sheet/SheetDescription.vue +23 -0
- package/app/components/ui/sheet/SheetFooter.vue +15 -0
- package/app/components/ui/sheet/SheetHeader.vue +15 -0
- package/app/components/ui/sheet/SheetOverlay.vue +28 -0
- package/app/components/ui/sheet/SheetTitle.vue +23 -0
- package/app/components/ui/sheet/SheetTrigger.vue +12 -0
- package/app/components/ui/sheet/index.ts +8 -0
- package/app/components/ui/sidebar/Sidebar.vue +110 -0
- package/app/components/ui/sidebar/SidebarContent.vue +23 -0
- package/app/components/ui/sidebar/SidebarFooter.vue +18 -0
- package/app/components/ui/sidebar/SidebarGroup.vue +18 -0
- package/app/components/ui/sidebar/SidebarGroupAction.vue +31 -0
- package/app/components/ui/sidebar/SidebarGroupContent.vue +18 -0
- package/app/components/ui/sidebar/SidebarGroupLabel.vue +30 -0
- package/app/components/ui/sidebar/SidebarHeader.vue +18 -0
- package/app/components/ui/sidebar/SidebarInput.vue +19 -0
- package/app/components/ui/sidebar/SidebarInset.vue +23 -0
- package/app/components/ui/sidebar/SidebarMenu.vue +18 -0
- package/app/components/ui/sidebar/SidebarMenuAction.vue +42 -0
- package/app/components/ui/sidebar/SidebarMenuBadge.vue +28 -0
- package/app/components/ui/sidebar/SidebarMenuButton.vue +60 -0
- package/app/components/ui/sidebar/SidebarMenuButtonChild.vue +36 -0
- package/app/components/ui/sidebar/SidebarMenuItem.vue +18 -0
- package/app/components/ui/sidebar/SidebarMenuSkeleton.vue +35 -0
- package/app/components/ui/sidebar/SidebarMenuSub.vue +24 -0
- package/app/components/ui/sidebar/SidebarMenuSubButton.vue +43 -0
- package/app/components/ui/sidebar/SidebarMenuSubItem.vue +18 -0
- package/app/components/ui/sidebar/SidebarProvider.vue +109 -0
- package/app/components/ui/sidebar/SidebarRail.vue +35 -0
- package/app/components/ui/sidebar/SidebarSeparator.vue +19 -0
- package/app/components/ui/sidebar/SidebarTrigger.vue +26 -0
- package/app/components/ui/sidebar/index.ts +62 -0
- package/app/components/ui/sidebar/utils.ts +19 -0
- package/app/components/ui/skeleton/Skeleton.vue +17 -0
- package/app/components/ui/skeleton/index.ts +1 -0
- package/app/components/ui/sonner/Sonner.vue +67 -0
- package/app/components/ui/sonner/index.ts +1 -0
- package/app/components/ui/tabs/Tabs.vue +26 -0
- package/app/components/ui/tabs/TabsContent.vue +23 -0
- package/app/components/ui/tabs/TabsList.vue +28 -0
- package/app/components/ui/tabs/TabsTrigger.vue +30 -0
- package/app/components/ui/tabs/index.ts +4 -0
- package/app/components/ui/textarea/Textarea.vue +32 -0
- package/app/components/ui/textarea/index.ts +1 -0
- package/app/components/ui/tooltip/Tooltip.vue +15 -0
- package/app/components/ui/tooltip/TooltipContent.vue +49 -0
- package/app/components/ui/tooltip/TooltipProvider.vue +14 -0
- package/app/components/ui/tooltip/TooltipTrigger.vue +12 -0
- package/app/components/ui/tooltip/index.ts +4 -0
- package/app/composables/useActiveHeading.ts +90 -0
- package/app/composables/useDuxtAnimatedHeight.ts +155 -0
- package/app/composables/useDuxtBreadcrumb.ts +60 -0
- package/app/composables/useDuxtChoice.ts +28 -0
- package/app/composables/useDuxtCollection.ts +89 -0
- package/app/composables/useDuxtConfig.ts +47 -0
- package/app/composables/useDuxtNavigation.ts +87 -0
- package/app/composables/useDuxtPageFocus.ts +30 -0
- package/app/composables/useDuxtPath.ts +42 -0
- package/app/composables/useDuxtSearch.ts +208 -0
- package/app/composables/useDuxtSection.ts +44 -0
- package/app/composables/useDuxtSectionRow.ts +49 -0
- package/app/composables/useDuxtShortcuts.ts +58 -0
- package/app/composables/useDuxtSiteUrl.ts +27 -0
- package/app/composables/useDuxtToast.ts +25 -0
- package/app/composables/useDuxtVersion.ts +138 -0
- package/app/composables/useFuzzySearch.ts +83 -0
- package/app/composables/usePackageManager.ts +4 -0
- package/app/composables/useRecentPages.ts +52 -0
- package/app/error.vue +133 -0
- package/app/layouts/changelog.vue +85 -0
- package/app/layouts/default.vue +23 -0
- package/app/layouts/docs.vue +62 -0
- package/app/layouts/reference.vue +85 -0
- package/app/lib/utils.ts +7 -0
- package/app/middleware/duxt-section-layout.global.ts +57 -0
- package/app/pages/[...slug].vue +441 -0
- package/app/pages/index.vue +479 -0
- package/app/types/duxt.d.ts +955 -0
- package/app/utils/aside.ts +16 -0
- package/app/utils/changelog.ts +182 -0
- package/app/utils/code-highlight.ts +116 -0
- package/app/utils/duxt-config.ts +185 -0
- package/app/utils/duxt-text.ts +135 -0
- package/app/utils/file-icons.ts +62 -0
- package/app/utils/generated-sections.ts +190 -0
- package/app/utils/generated-toc.ts +216 -0
- package/app/utils/heading-link.ts +32 -0
- package/app/utils/locale-path.ts +45 -0
- package/app/utils/navigation-tree.ts +206 -0
- package/app/utils/nearest-page.ts +72 -0
- package/app/utils/openapi.ts +1028 -0
- package/app/utils/package-command.ts +162 -0
- package/app/utils/request-samples.ts +635 -0
- package/app/utils/section-scope.ts +97 -0
- package/app/utils/sheet-navigation.ts +100 -0
- package/app/utils/source-file.ts +84 -0
- package/app/utils/version-choices.ts +68 -0
- package/app/utils/version-paths.ts +65 -0
- package/bin/duxt-report.mjs +45 -0
- package/components.json +19 -0
- package/content-cache.ts +193 -0
- package/content.config.ts +38 -0
- package/duxt-app-config.ts +124 -0
- package/highlight-langs.ts +68 -0
- package/i18n/i18n.config.ts +16 -0
- package/i18n/locales/de/duxt/changelog.json +17 -0
- package/i18n/locales/de/duxt/code.json +13 -0
- package/i18n/locales/de/duxt/defaults.json +22 -0
- package/i18n/locales/de/duxt/devtools.json +7 -0
- package/i18n/locales/de/duxt/error.json +10 -0
- package/i18n/locales/de/duxt/footer.json +7 -0
- package/i18n/locales/de/duxt/locale.json +7 -0
- package/i18n/locales/de/duxt/nav.json +17 -0
- package/i18n/locales/de/duxt/openapi.json +89 -0
- package/i18n/locales/de/duxt/page.json +34 -0
- package/i18n/locales/de/duxt/search.json +12 -0
- package/i18n/locales/de/duxt/shortcuts.json +11 -0
- package/i18n/locales/de/duxt/theme.json +7 -0
- package/i18n/locales/de/duxt/toc.json +7 -0
- package/i18n/locales/de/duxt/version.json +25 -0
- package/i18n/locales/en/duxt/changelog.json +17 -0
- package/i18n/locales/en/duxt/code.json +13 -0
- package/i18n/locales/en/duxt/defaults.json +22 -0
- package/i18n/locales/en/duxt/devtools.json +7 -0
- package/i18n/locales/en/duxt/error.json +10 -0
- package/i18n/locales/en/duxt/footer.json +7 -0
- package/i18n/locales/en/duxt/locale.json +7 -0
- package/i18n/locales/en/duxt/nav.json +17 -0
- package/i18n/locales/en/duxt/openapi.json +89 -0
- package/i18n/locales/en/duxt/page.json +34 -0
- package/i18n/locales/en/duxt/search.json +12 -0
- package/i18n/locales/en/duxt/shortcuts.json +11 -0
- package/i18n/locales/en/duxt/theme.json +7 -0
- package/i18n/locales/en/duxt/toc.json +7 -0
- package/i18n/locales/en/duxt/version.json +25 -0
- package/i18n/locales/es/duxt/changelog.json +17 -0
- package/i18n/locales/es/duxt/code.json +13 -0
- package/i18n/locales/es/duxt/defaults.json +22 -0
- package/i18n/locales/es/duxt/devtools.json +7 -0
- package/i18n/locales/es/duxt/error.json +10 -0
- package/i18n/locales/es/duxt/footer.json +7 -0
- package/i18n/locales/es/duxt/locale.json +7 -0
- package/i18n/locales/es/duxt/nav.json +17 -0
- package/i18n/locales/es/duxt/openapi.json +89 -0
- package/i18n/locales/es/duxt/page.json +34 -0
- package/i18n/locales/es/duxt/search.json +12 -0
- package/i18n/locales/es/duxt/shortcuts.json +11 -0
- package/i18n/locales/es/duxt/theme.json +7 -0
- package/i18n/locales/es/duxt/toc.json +7 -0
- package/i18n/locales/es/duxt/version.json +25 -0
- package/i18n/locales/fr/duxt/changelog.json +17 -0
- package/i18n/locales/fr/duxt/code.json +13 -0
- package/i18n/locales/fr/duxt/defaults.json +22 -0
- package/i18n/locales/fr/duxt/devtools.json +7 -0
- package/i18n/locales/fr/duxt/error.json +10 -0
- package/i18n/locales/fr/duxt/footer.json +7 -0
- package/i18n/locales/fr/duxt/locale.json +7 -0
- package/i18n/locales/fr/duxt/nav.json +17 -0
- package/i18n/locales/fr/duxt/openapi.json +89 -0
- package/i18n/locales/fr/duxt/page.json +34 -0
- package/i18n/locales/fr/duxt/search.json +12 -0
- package/i18n/locales/fr/duxt/shortcuts.json +11 -0
- package/i18n/locales/fr/duxt/theme.json +7 -0
- package/i18n/locales/fr/duxt/toc.json +7 -0
- package/i18n/locales/fr/duxt/version.json +25 -0
- package/i18n/locales/pt/duxt/changelog.json +17 -0
- package/i18n/locales/pt/duxt/code.json +13 -0
- package/i18n/locales/pt/duxt/defaults.json +22 -0
- package/i18n/locales/pt/duxt/devtools.json +7 -0
- package/i18n/locales/pt/duxt/error.json +10 -0
- package/i18n/locales/pt/duxt/footer.json +7 -0
- package/i18n/locales/pt/duxt/locale.json +7 -0
- package/i18n/locales/pt/duxt/nav.json +17 -0
- package/i18n/locales/pt/duxt/openapi.json +89 -0
- package/i18n/locales/pt/duxt/page.json +34 -0
- package/i18n/locales/pt/duxt/search.json +12 -0
- package/i18n/locales/pt/duxt/shortcuts.json +11 -0
- package/i18n/locales/pt/duxt/theme.json +7 -0
- package/i18n/locales/pt/duxt/toc.json +7 -0
- package/i18n/locales/pt/duxt/version.json +25 -0
- package/i18n/locales/pt-BR/duxt/error.json +8 -0
- package/i18n/locales/pt-BR/duxt/nav.json +10 -0
- package/i18n/locales/pt-BR/duxt/page.json +10 -0
- package/i18n/locales/pt-BR/duxt/search.json +11 -0
- package/i18n/locales/pt-BR/duxt/version.json +13 -0
- package/mdc.config.ts +28 -0
- package/modules/config.ts +440 -0
- package/modules/devtools.ts +126 -0
- package/modules/git-meta.ts +349 -0
- package/modules/redirects.ts +152 -0
- package/modules/validate.ts +137 -0
- package/nuxt.config.ts +530 -0
- package/openapi-model.ts +358 -0
- package/openapi-parse.ts +1052 -0
- package/package.json +161 -0
- package/public/devtools/cache.html +318 -0
- package/public/devtools/checks.html +309 -0
- package/public/devtools/config.html +461 -0
- package/public/devtools/i18n.html +309 -0
- package/public/devtools/pages.html +348 -0
- package/public/devtools/paths.html +315 -0
- package/public/devtools/redirects.html +309 -0
- package/public/devtools/search.html +324 -0
- package/public/devtools/sources.html +309 -0
- package/public/devtools/versions.html +318 -0
- package/report.ts +303 -0
- package/repository-root.ts +26 -0
- package/section-reports.ts +55 -0
- package/sections-changelog.ts +644 -0
- package/sections-openapi.ts +450 -0
- package/sections-resolve.ts +711 -0
- package/sections.ts +157 -0
- package/server/devtools/content.ts +120 -0
- package/server/devtools/context.ts +38 -0
- package/server/devtools/entry-path.ts +30 -0
- package/server/devtools/handler.ts +104 -0
- package/server/devtools/preview.ts +461 -0
- package/server/devtools/render/content.ts +233 -0
- package/server/devtools/render/sources.ts +414 -0
- package/server/devtools/render/system.ts +352 -0
- package/server/devtools/shell.ts +611 -0
- package/server/devtools/sources.ts +122 -0
- package/server/devtools/system.ts +178 -0
- package/server/mcp/tools/list-pages.ts +52 -0
- package/server/mcp/tools/read-page.ts +57 -0
- package/server/mcp/tools/search-docs.ts +74 -0
- package/server/middleware/raw-markdown.ts +86 -0
- package/server/routes/llms-full.txt.get.ts +75 -0
- package/server/routes/llms.txt.get.ts +63 -0
- package/server/routes/rss.xml.get.ts +125 -0
- package/server/utils/duxt-server-text.ts +46 -0
- package/sources-git.ts +113 -0
- package/sources-resolve.ts +908 -0
- package/sources.ts +287 -0
- package/validate-report.ts +494 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import type { ContentNavigationItem } from '@nuxt/content';
|
|
2
|
+
|
|
3
|
+
/** Find a node by path, anywhere in the tree. */
|
|
4
|
+
export function findByPath(
|
|
5
|
+
items: ContentNavigationItem[],
|
|
6
|
+
path: string
|
|
7
|
+
): ContentNavigationItem | undefined {
|
|
8
|
+
for (const item of items) {
|
|
9
|
+
if (item.path === path) return item;
|
|
10
|
+
|
|
11
|
+
const inside = item.children?.length
|
|
12
|
+
? findByPath(item.children, path)
|
|
13
|
+
: undefined;
|
|
14
|
+
if (inside) return inside;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The entries a section's sidebar should show.
|
|
22
|
+
*
|
|
23
|
+
* A multi-segment prefix makes Content wrap the tree in intermediate nodes —
|
|
24
|
+
* `/workflows/v0.7.0` gets a `/workflows` node above it — and rendering those
|
|
25
|
+
* gave a collapsible group whose only child was itself. So after picking the
|
|
26
|
+
* section's branch, walk down while there is exactly one node the route is
|
|
27
|
+
* still inside.
|
|
28
|
+
*
|
|
29
|
+
* A PAGE IN NO CONFIGURED SECTION STILL GETS ONE BRANCH, not the whole tree.
|
|
30
|
+
* `sections` is the consumer's reading order, and a tree holds folders it does
|
|
31
|
+
* not list — `99.adr/` is the standing example, an appendix by construction. The
|
|
32
|
+
* fallback used to hand back `tree`, so the sidebar on such a page listed every
|
|
33
|
+
* section and every page under all of them at once, while the page beside it
|
|
34
|
+
* showed one branch. That reads as the sidebar breaking rather than as a page
|
|
35
|
+
* being outside the reading order, and it gets worse the larger the tree is.
|
|
36
|
+
*
|
|
37
|
+
* So the fallback narrows to the branch the route is actually in, and only
|
|
38
|
+
* reaches for the whole tree when the route matches no top-level node at all —
|
|
39
|
+
* a site with no sections configured, where the whole tree IS the branch.
|
|
40
|
+
*/
|
|
41
|
+
export function sectionItems(
|
|
42
|
+
tree: ContentNavigationItem[],
|
|
43
|
+
sectionPath: string | undefined,
|
|
44
|
+
routePath: string
|
|
45
|
+
): ContentNavigationItem[] {
|
|
46
|
+
const branch =
|
|
47
|
+
(sectionPath ? findByPath(tree, sectionPath) : undefined) ??
|
|
48
|
+
// Longest match, so a nested folder wins over the one above it.
|
|
49
|
+
tree
|
|
50
|
+
.filter((node) => node.path && routePath.startsWith(node.path))
|
|
51
|
+
.sort((a, b) => (b.path?.length ?? 0) - (a.path?.length ?? 0))[0];
|
|
52
|
+
|
|
53
|
+
let items = branch?.children?.length
|
|
54
|
+
? branch.children
|
|
55
|
+
: branch
|
|
56
|
+
? [branch]
|
|
57
|
+
: tree;
|
|
58
|
+
|
|
59
|
+
while (items.length === 1) {
|
|
60
|
+
const [only] = items;
|
|
61
|
+
if (
|
|
62
|
+
!only?.children?.length ||
|
|
63
|
+
!only.path ||
|
|
64
|
+
!routePath.startsWith(only.path)
|
|
65
|
+
)
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
items = only.children;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return items;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The crumbs between the section and the page.
|
|
76
|
+
*
|
|
77
|
+
* Anything at or above the source's own prefix is one of those wrapper nodes,
|
|
78
|
+
* not a page anyone navigates to — it showed up as an extra crumb on a
|
|
79
|
+
* versioned URL that the unversioned one did not have.
|
|
80
|
+
*/
|
|
81
|
+
export function trailBelowPrefix(
|
|
82
|
+
tree: ContentNavigationItem[],
|
|
83
|
+
path: string,
|
|
84
|
+
prefix: string
|
|
85
|
+
): ContentNavigationItem[] {
|
|
86
|
+
const found: ContentNavigationItem[] = [];
|
|
87
|
+
|
|
88
|
+
const walk = (
|
|
89
|
+
items: ContentNavigationItem[],
|
|
90
|
+
ancestors: ContentNavigationItem[]
|
|
91
|
+
): boolean => {
|
|
92
|
+
for (const item of items) {
|
|
93
|
+
const chain = [...ancestors, item];
|
|
94
|
+
|
|
95
|
+
if (item.path === path) {
|
|
96
|
+
found.push(...chain);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (item.children?.length && walk(item.children, chain)) return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return false;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
walk(tree, []);
|
|
107
|
+
|
|
108
|
+
return found.filter((item) => (item.path?.length ?? 0) > prefix.length);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The original's tree, wearing the titles a translation has for it.
|
|
113
|
+
*
|
|
114
|
+
* A translated tree is almost never complete — two pages of forty is the normal
|
|
115
|
+
* state of every documentation project that has ever tried this — and both
|
|
116
|
+
* obvious answers are wrong. Building the sidebar from the TRANSLATION hides
|
|
117
|
+
* every page that has none, although the fallback serves them perfectly well;
|
|
118
|
+
* building it from the ORIGINAL leaves a reader in German looking at an
|
|
119
|
+
* English table of contents.
|
|
120
|
+
*
|
|
121
|
+
* So the structure comes from the original, which is complete by definition,
|
|
122
|
+
* and each node takes the translated title and description where one exists.
|
|
123
|
+
* The sidebar is then always navigable and says, entry by entry, how far the
|
|
124
|
+
* translation has got.
|
|
125
|
+
*/
|
|
126
|
+
export function overlayTranslations(
|
|
127
|
+
tree: ContentNavigationItem[],
|
|
128
|
+
translated: Map<string, { title?: string; description?: string }>
|
|
129
|
+
): ContentNavigationItem[] {
|
|
130
|
+
return tree.map((item) => {
|
|
131
|
+
const match = item.path ? translated.get(item.path) : undefined;
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
...item,
|
|
135
|
+
...(match?.title ? { title: match.title } : {}),
|
|
136
|
+
...(match?.description ? { description: match.description } : {}),
|
|
137
|
+
...(item.children?.length
|
|
138
|
+
? { children: overlayTranslations(item.children, translated) }
|
|
139
|
+
: {})
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* A folder wearing the title of its own index page.
|
|
146
|
+
*
|
|
147
|
+
* Content names a directory node after the DIRECTORY, so `99.adr/` arrives as
|
|
148
|
+
* "Adr" — while the `index.md` inside it, which is the page that node leads to,
|
|
149
|
+
* says "Architecture decisions". The two are the same destination under two
|
|
150
|
+
* names, and the shorter one is a slug rather than a title: the sidebar grouped
|
|
151
|
+
* eight records under "Adr" and the breadcrumb read "Adr › …".
|
|
152
|
+
*
|
|
153
|
+
* It only shows where a folder's name and its index title differ, which is why
|
|
154
|
+
* it went unnoticed — `4.reference/` is "Reference" either way. An abbreviated
|
|
155
|
+
* or hyphenated folder is where it bites, and a consumer's tree is full of
|
|
156
|
+
* those.
|
|
157
|
+
*
|
|
158
|
+
* The index is identified by carrying the FOLDER'S OWN path: Content emits it
|
|
159
|
+
* as a child of the node, not as a sibling. Nothing else in the tree can hold
|
|
160
|
+
* that path, so there is no ambiguity to resolve.
|
|
161
|
+
*
|
|
162
|
+
* Applied to the base tree BEFORE any translation overlay, so a locale that
|
|
163
|
+
* translates the index title carries it up here too.
|
|
164
|
+
*/
|
|
165
|
+
export function titleFoldersFromIndex(
|
|
166
|
+
items: ContentNavigationItem[]
|
|
167
|
+
): ContentNavigationItem[] {
|
|
168
|
+
return items.map((item) => {
|
|
169
|
+
if (!item.children?.length) return item;
|
|
170
|
+
|
|
171
|
+
const index = item.children.find((child) => child.path === item.path);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
...item,
|
|
175
|
+
...(index?.title ? { title: index.title } : {}),
|
|
176
|
+
children: titleFoldersFromIndex(item.children)
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The icon a navigation entry draws, if any.
|
|
183
|
+
*
|
|
184
|
+
* Three levels, most specific first: the page's own frontmatter, the `pageIcon`
|
|
185
|
+
* of the section it sits in, then `duxt.pageIcon` for the whole site. Absent at
|
|
186
|
+
* every level means no icon, which is the right default — one symbol repeated
|
|
187
|
+
* down a whole sidebar distinguishes nothing.
|
|
188
|
+
*
|
|
189
|
+
* The section is matched by path prefix, longest first, so a section nested
|
|
190
|
+
* under another wins over the one above it. Pure, and separated from the
|
|
191
|
+
* components that call it, because the precedence is the part worth testing —
|
|
192
|
+
* rendering an `<Icon>` is not.
|
|
193
|
+
*/
|
|
194
|
+
export function resolvePageIcon(
|
|
195
|
+
item: { icon?: unknown; path?: string },
|
|
196
|
+
sections: { to?: string; pageIcon?: string }[] | undefined,
|
|
197
|
+
fallback: string | undefined
|
|
198
|
+
): string | undefined {
|
|
199
|
+
if (typeof item.icon === 'string' && item.icon) return item.icon;
|
|
200
|
+
|
|
201
|
+
const section = (sections ?? [])
|
|
202
|
+
.filter((entry) => entry.to && item.path?.startsWith(entry.to))
|
|
203
|
+
.sort((a, b) => (b.to?.length ?? 0) - (a.to?.length ?? 0))[0];
|
|
204
|
+
|
|
205
|
+
return section?.pageIcon ?? fallback;
|
|
206
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page a reader most likely meant.
|
|
3
|
+
*
|
|
4
|
+
* A 404 in versioned documentation is usually a URL that is nearly right: a
|
|
5
|
+
* segment renamed between versions, a guessed path, a link with the version
|
|
6
|
+
* prefix missing. An empty "not found" tells that reader nothing; the nearest
|
|
7
|
+
* few real paths usually tell them everything.
|
|
8
|
+
*
|
|
9
|
+
* Scored on the SEGMENTS, not on the whole string. `/duxt/guide/deploying` and
|
|
10
|
+
* `/workflows/guide/deploying` are one segment apart and read as the same page
|
|
11
|
+
* in another place, which edit distance over the raw text would rank far below
|
|
12
|
+
* a page whose name happens to share letters.
|
|
13
|
+
*/
|
|
14
|
+
export function nearestPages(
|
|
15
|
+
wanted: string,
|
|
16
|
+
candidates: { path: string; title?: string }[],
|
|
17
|
+
limit = 4
|
|
18
|
+
): { path: string; title?: string }[] {
|
|
19
|
+
const target = segments(wanted);
|
|
20
|
+
if (!target.length) return [];
|
|
21
|
+
|
|
22
|
+
return candidates
|
|
23
|
+
.filter((candidate) => candidate.path && candidate.path !== wanted)
|
|
24
|
+
.map((candidate) => ({
|
|
25
|
+
candidate,
|
|
26
|
+
score: score(target, segments(candidate.path))
|
|
27
|
+
}))
|
|
28
|
+
.filter((entry) => entry.score > 0)
|
|
29
|
+
.sort(
|
|
30
|
+
(a, b) =>
|
|
31
|
+
b.score - a.score || a.candidate.path.localeCompare(b.candidate.path)
|
|
32
|
+
)
|
|
33
|
+
.slice(0, limit)
|
|
34
|
+
.map((entry) => entry.candidate);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const segments = (path: string) => path.split('/').filter(Boolean);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* How much two paths have in common.
|
|
41
|
+
*
|
|
42
|
+
* The LAST segment is what the reader was actually after, so a match there is
|
|
43
|
+
* worth more than one anywhere else; a shared prefix comes second, because it
|
|
44
|
+
* says the reader was in the right part of the tree. A near-miss on the last
|
|
45
|
+
* segment — one being contained in the other — still counts, which is what
|
|
46
|
+
* catches a renamed page.
|
|
47
|
+
*/
|
|
48
|
+
function score(wanted: string[], candidate: string[]): number {
|
|
49
|
+
let points = 0;
|
|
50
|
+
|
|
51
|
+
const wantedLast = wanted.at(-1)!;
|
|
52
|
+
const candidateLast = candidate.at(-1);
|
|
53
|
+
|
|
54
|
+
if (candidateLast === wantedLast) points += 10;
|
|
55
|
+
else if (
|
|
56
|
+
candidateLast &&
|
|
57
|
+
(candidateLast.includes(wantedLast) || wantedLast.includes(candidateLast))
|
|
58
|
+
) {
|
|
59
|
+
points += 5;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const [index, segment] of wanted.entries()) {
|
|
63
|
+
if (candidate[index] === segment) points += 2;
|
|
64
|
+
else break;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
for (const segment of wanted) {
|
|
68
|
+
if (segment !== wantedLast && candidate.includes(segment)) points += 1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return points;
|
|
72
|
+
}
|