@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,359 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* A JSON box that knows it is JSON: highlighting, brackets, and the parse error
|
|
4
|
+
* underlined where it is rather than reported after a 400.
|
|
5
|
+
*
|
|
6
|
+
* WHY NOT A TEXTAREA, which is what this was: the request body is the one field
|
|
7
|
+
* in the try-it client a reader types more than a word into, and a textarea
|
|
8
|
+
* says nothing until the server answers. A missing comma is a round trip and a
|
|
9
|
+
* guess; here it is a red squiggle under the character that caused it.
|
|
10
|
+
*
|
|
11
|
+
* WHY NOT MONACO, which is the obvious name: it is an editor for editing files,
|
|
12
|
+
* and it arrives with workers and several megabytes. This layer is a package
|
|
13
|
+
* every consumer site carries, including the ones with no OpenAPI source at
|
|
14
|
+
* all. CodeMirror 6 does the same job for this box at roughly a tenth of it —
|
|
15
|
+
* and it is loaded the way `Mermaid.vue` loads mermaid: on demand, in the
|
|
16
|
+
* browser, never in the bundle a page without it downloads.
|
|
17
|
+
*
|
|
18
|
+
* The TEXTAREA IS STILL HERE, under `<ClientOnly>`'s fallback and after a
|
|
19
|
+
* failed import. The body of a request is not decoration: a reader whose
|
|
20
|
+
* network dropped the chunk, or who has JavaScript half-loaded, still has to be
|
|
21
|
+
* able to type into it.
|
|
22
|
+
*/
|
|
23
|
+
import type { EditorView as EditorViewType } from '@codemirror/view';
|
|
24
|
+
import type { DuxtOpenApiKey } from '@duxt/utils/openapi';
|
|
25
|
+
|
|
26
|
+
const model = defineModel<string>({ default: '' });
|
|
27
|
+
|
|
28
|
+
const props = withDefaults(
|
|
29
|
+
defineProps<{
|
|
30
|
+
ariaLabel?: string;
|
|
31
|
+
minHeight?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The keys this document says the body may carry.
|
|
34
|
+
*
|
|
35
|
+
* What turns a JSON box into one that knows the API: the completion list
|
|
36
|
+
* offers them, and the linter marks a required one that is absent and a
|
|
37
|
+
* key the document never described. Empty is a working editor with plain
|
|
38
|
+
* JSON checking and nothing added — which is the honest state for a body
|
|
39
|
+
* whose shape the document does not fix. See `openApiBodyKeys`.
|
|
40
|
+
*/
|
|
41
|
+
keys?: DuxtOpenApiKey[];
|
|
42
|
+
}>(),
|
|
43
|
+
{ minHeight: '12rem', keys: () => [] }
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const container = useTemplateRef<HTMLElement>('container');
|
|
47
|
+
const colorMode = useColorMode();
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The keys, read through a ref inside the extensions.
|
|
51
|
+
*
|
|
52
|
+
* A CodeMirror extension is built once and holds whatever it closed over, so
|
|
53
|
+
* reading the prop directly would freeze the schema of whichever media type
|
|
54
|
+
* was selected when the editor mounted. Through the ref, switching the media
|
|
55
|
+
* type above re-lints and re-completes with no rebuild.
|
|
56
|
+
*/
|
|
57
|
+
const keysRef = computed(() => props.keys);
|
|
58
|
+
|
|
59
|
+
/** Filled once the editor is up; until then the fallback box is what shows. */
|
|
60
|
+
const ready = ref(false);
|
|
61
|
+
let view: EditorViewType | undefined;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The palette, taken from the page rather than from a CodeMirror theme.
|
|
65
|
+
*
|
|
66
|
+
* A bundled theme would be a second set of colours beside the one the site
|
|
67
|
+
* already defines, and it would not follow a consumer who redefined a token.
|
|
68
|
+
* `color-mode` swaps the variables underneath, so the editor follows the theme
|
|
69
|
+
* with no second render — which is why there is no light/dark theme here at
|
|
70
|
+
* all, only `dark: …` for the syntax layer's own decision.
|
|
71
|
+
*/
|
|
72
|
+
async function mount() {
|
|
73
|
+
if (!import.meta.client || !container.value || view) return;
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const [
|
|
77
|
+
{ EditorState },
|
|
78
|
+
view_,
|
|
79
|
+
json,
|
|
80
|
+
lint,
|
|
81
|
+
commands,
|
|
82
|
+
language,
|
|
83
|
+
autocomplete,
|
|
84
|
+
{ tags }
|
|
85
|
+
] = await Promise.all([
|
|
86
|
+
import('@codemirror/state'),
|
|
87
|
+
import('@codemirror/view'),
|
|
88
|
+
import('@codemirror/lang-json'),
|
|
89
|
+
import('@codemirror/lint'),
|
|
90
|
+
import('@codemirror/commands'),
|
|
91
|
+
import('@codemirror/language'),
|
|
92
|
+
import('@codemirror/autocomplete'),
|
|
93
|
+
import('@lezer/highlight')
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
const { EditorView, keymap, lineNumbers, highlightActiveLine } = view_;
|
|
97
|
+
|
|
98
|
+
const theme = EditorView.theme(
|
|
99
|
+
{
|
|
100
|
+
'&': {
|
|
101
|
+
fontSize: '0.75rem',
|
|
102
|
+
backgroundColor: 'transparent',
|
|
103
|
+
color: 'var(--foreground)'
|
|
104
|
+
},
|
|
105
|
+
'&.cm-focused': { outline: 'none' },
|
|
106
|
+
'.cm-content': {
|
|
107
|
+
fontFamily: 'var(--font-mono, ui-monospace, monospace)',
|
|
108
|
+
padding: '0.75rem 0'
|
|
109
|
+
},
|
|
110
|
+
// CodeMirror's own defaults put the numbers against the border and the
|
|
111
|
+
// text 2px off the gutter. A box a reader types JSON into wants the
|
|
112
|
+
// same breathing room the code blocks on the page have.
|
|
113
|
+
'.cm-line': { padding: '0 0.75rem' },
|
|
114
|
+
'.cm-gutters': {
|
|
115
|
+
backgroundColor: 'transparent',
|
|
116
|
+
color: 'var(--muted-foreground)',
|
|
117
|
+
border: 'none',
|
|
118
|
+
paddingLeft: '0.5rem'
|
|
119
|
+
},
|
|
120
|
+
'.cm-lineNumbers .cm-gutterElement': { paddingRight: '0.5rem' },
|
|
121
|
+
'.cm-activeLine': {
|
|
122
|
+
backgroundColor: 'color-mix(in oklab, var(--muted) 45%, transparent)'
|
|
123
|
+
},
|
|
124
|
+
'.cm-activeLineGutter': { backgroundColor: 'transparent' },
|
|
125
|
+
'.cm-scroller': { overflow: 'auto' },
|
|
126
|
+
'.cm-lintRange-error': {
|
|
127
|
+
textDecoration: 'underline wavy var(--destructive)'
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
{ dark: colorMode.value === 'dark' }
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
view = new EditorView({
|
|
134
|
+
parent: container.value,
|
|
135
|
+
state: EditorState.create({
|
|
136
|
+
doc: model.value,
|
|
137
|
+
extensions: [
|
|
138
|
+
lineNumbers(),
|
|
139
|
+
highlightActiveLine(),
|
|
140
|
+
language.bracketMatching(),
|
|
141
|
+
language.syntaxHighlighting(
|
|
142
|
+
language.HighlightStyle.define([
|
|
143
|
+
{ tag: tags.propertyName, color: 'var(--duxt-cm-key)' },
|
|
144
|
+
{ tag: tags.string, color: 'var(--duxt-cm-string)' },
|
|
145
|
+
{ tag: tags.number, color: 'var(--duxt-cm-value)' },
|
|
146
|
+
{ tag: tags.bool, color: 'var(--duxt-cm-value)' },
|
|
147
|
+
{ tag: tags.null, color: 'var(--duxt-cm-value)' },
|
|
148
|
+
{ tag: tags.keyword, color: 'var(--duxt-cm-keyword)' },
|
|
149
|
+
{ tag: tags.comment, color: 'var(--duxt-cm-comment)' }
|
|
150
|
+
]),
|
|
151
|
+
{ fallback: true }
|
|
152
|
+
),
|
|
153
|
+
json.json(),
|
|
154
|
+
lint.linter(json.jsonParseLinter()),
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The DOCUMENT's own rules, beside the syntax ones.
|
|
158
|
+
*
|
|
159
|
+
* A second linter rather than one that does both: `jsonParseLinter`
|
|
160
|
+
* answers "is this JSON", this answers "is this the body this
|
|
161
|
+
* endpoint described", and the first has to keep working when the
|
|
162
|
+
* schema says nothing at all. The arithmetic is pure and tested —
|
|
163
|
+
* see `openApiBodyProblems`.
|
|
164
|
+
*/
|
|
165
|
+
lint.linter((view) => {
|
|
166
|
+
const text = view.state.doc.toString();
|
|
167
|
+
|
|
168
|
+
return openApiBodyProblems(text, keysRef.value).map((problem) => ({
|
|
169
|
+
from: Math.min(problem.from, text.length),
|
|
170
|
+
to: Math.min(problem.to, text.length),
|
|
171
|
+
severity: problem.severity,
|
|
172
|
+
message: problem.message
|
|
173
|
+
}));
|
|
174
|
+
}),
|
|
175
|
+
lint.lintGutter(),
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The keys the document names, offered WHERE A KEY GOES.
|
|
179
|
+
*
|
|
180
|
+
* Read off the syntax tree rather than off the characters before
|
|
181
|
+
* the cursor, which is what the first attempt did and why it only
|
|
182
|
+
* sometimes worked: matching an open quote fires inside a string
|
|
183
|
+
* VALUE too, so it offered key names where a value goes — and it
|
|
184
|
+
* stayed silent directly after a `{` or a `,`, which is exactly
|
|
185
|
+
* where the next key begins. The tree knows which of the three the
|
|
186
|
+
* cursor is in.
|
|
187
|
+
*
|
|
188
|
+
* A value inside a property whose schema named an `enum` gets that
|
|
189
|
+
* enum instead — a field the document restricted to three words is a
|
|
190
|
+
* field worth being handed those three words.
|
|
191
|
+
*/
|
|
192
|
+
autocomplete.autocompletion({
|
|
193
|
+
override: [
|
|
194
|
+
(context) => {
|
|
195
|
+
const keys = keysRef.value;
|
|
196
|
+
if (!keys.length) return null;
|
|
197
|
+
|
|
198
|
+
const tree = language.syntaxTree(context.state);
|
|
199
|
+
const node = tree.resolveInner(context.pos, -1);
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The object the cursor is in, found by WALKING UP.
|
|
203
|
+
*
|
|
204
|
+
* A body being typed is a broken document almost all of the
|
|
205
|
+
* time — a half-written key, no comma yet, no closing brace —
|
|
206
|
+
* and lezer answers with error nodes rather than the tidy
|
|
207
|
+
* `Property` a finished line produces. Asking "is this node a
|
|
208
|
+
* property name" therefore said no exactly while the reader was
|
|
209
|
+
* typing one, which is the whole moment this exists for.
|
|
210
|
+
* Walking up to the nearest object works on the broken document
|
|
211
|
+
* as well as on the finished one.
|
|
212
|
+
*/
|
|
213
|
+
let scope = node;
|
|
214
|
+
while (scope.parent && scope.name !== 'Object') {
|
|
215
|
+
scope = scope.parent;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (scope.name !== 'Object') return null;
|
|
219
|
+
|
|
220
|
+
/** Inside a string: its own span is what gets replaced. */
|
|
221
|
+
const inString =
|
|
222
|
+
node.name === 'String' || node.name === 'PropertyName';
|
|
223
|
+
const from = inString ? node.from : context.pos;
|
|
224
|
+
const to = inString ? node.to : context.pos;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The property this string is the VALUE of, if it is one.
|
|
228
|
+
*
|
|
229
|
+
* Everything else in an object is a key: a finished
|
|
230
|
+
* `PropertyName`, a string with no property around it yet, and
|
|
231
|
+
* the empty spot after `{` or a comma.
|
|
232
|
+
*/
|
|
233
|
+
const property =
|
|
234
|
+
node.parent?.name === 'Property' ? node.parent : undefined;
|
|
235
|
+
|
|
236
|
+
const namingKey =
|
|
237
|
+
!property ||
|
|
238
|
+
!property.firstChild ||
|
|
239
|
+
node.from <= property.firstChild.to;
|
|
240
|
+
|
|
241
|
+
if (namingKey) {
|
|
242
|
+
return {
|
|
243
|
+
from,
|
|
244
|
+
to,
|
|
245
|
+
options: keys.map((key) => ({
|
|
246
|
+
label: JSON.stringify(key.name),
|
|
247
|
+
type: 'property',
|
|
248
|
+
detail: key.required
|
|
249
|
+
? `${key.type} · required`
|
|
250
|
+
: key.type,
|
|
251
|
+
info: key.description
|
|
252
|
+
}))
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// A value: which property is it the value of?
|
|
257
|
+
if (!inString || !property?.firstChild) return null;
|
|
258
|
+
|
|
259
|
+
const name = context.state.sliceDoc(
|
|
260
|
+
property.firstChild.from,
|
|
261
|
+
property.firstChild.to
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const options = keys.find(
|
|
265
|
+
(key) => JSON.stringify(key.name) === name
|
|
266
|
+
)?.enum;
|
|
267
|
+
|
|
268
|
+
return options?.length
|
|
269
|
+
? {
|
|
270
|
+
from,
|
|
271
|
+
to,
|
|
272
|
+
options: options.map((value) => ({
|
|
273
|
+
label: JSON.stringify(value),
|
|
274
|
+
type: 'enum'
|
|
275
|
+
}))
|
|
276
|
+
}
|
|
277
|
+
: null;
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
}),
|
|
281
|
+
|
|
282
|
+
keymap.of([
|
|
283
|
+
...commands.defaultKeymap,
|
|
284
|
+
...autocomplete.completionKeymap,
|
|
285
|
+
...lint.lintKeymap
|
|
286
|
+
]),
|
|
287
|
+
EditorView.lineWrapping,
|
|
288
|
+
theme,
|
|
289
|
+
EditorView.contentAttributes.of(
|
|
290
|
+
props.ariaLabel ? { 'aria-label': props.ariaLabel } : {}
|
|
291
|
+
),
|
|
292
|
+
EditorView.updateListener.of((update) => {
|
|
293
|
+
if (update.docChanged) model.value = update.state.doc.toString();
|
|
294
|
+
})
|
|
295
|
+
]
|
|
296
|
+
})
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
ready.value = true;
|
|
300
|
+
} catch {
|
|
301
|
+
// The chunk did not arrive. The fallback box below is the whole answer:
|
|
302
|
+
// this is an input, and an input that fails to load must still be typable.
|
|
303
|
+
ready.value = false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
onMounted(mount);
|
|
308
|
+
|
|
309
|
+
onBeforeUnmount(() => {
|
|
310
|
+
view?.destroy();
|
|
311
|
+
view = undefined;
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* A change from OUTSIDE — the form view writing back, or the format button.
|
|
316
|
+
*
|
|
317
|
+
* Guarded against the echo of the editor's own edit: dispatching the document
|
|
318
|
+
* the editor already holds would move the cursor to the end on every keystroke.
|
|
319
|
+
*/
|
|
320
|
+
watch(model, (value) => {
|
|
321
|
+
if (!view || value === view.state.doc.toString()) return;
|
|
322
|
+
|
|
323
|
+
view.dispatch({
|
|
324
|
+
changes: { from: 0, to: view.state.doc.length, insert: value }
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
</script>
|
|
328
|
+
|
|
329
|
+
<template>
|
|
330
|
+
<div
|
|
331
|
+
class="overflow-hidden rounded-md border border-input bg-transparent shadow-sm focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50"
|
|
332
|
+
>
|
|
333
|
+
<div
|
|
334
|
+
ref="container"
|
|
335
|
+
class="duxt-json-editor"
|
|
336
|
+
:style="{ '--duxt-editor-min-height': minHeight }"
|
|
337
|
+
/>
|
|
338
|
+
|
|
339
|
+
<!-- Before the editor is up, and after it failed to arrive. -->
|
|
340
|
+
<UiTextarea
|
|
341
|
+
v-if="!ready"
|
|
342
|
+
v-model="model"
|
|
343
|
+
:aria-label="ariaLabel"
|
|
344
|
+
spellcheck="false"
|
|
345
|
+
class="rounded-none border-0 p-3 font-mono text-xs shadow-none focus-visible:ring-0"
|
|
346
|
+
:style="{ minHeight }"
|
|
347
|
+
/>
|
|
348
|
+
</div>
|
|
349
|
+
</template>
|
|
350
|
+
|
|
351
|
+
<style>
|
|
352
|
+
.duxt-json-editor .cm-editor {
|
|
353
|
+
min-height: var(--duxt-editor-min-height);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.duxt-json-editor .cm-scroller {
|
|
357
|
+
max-height: 24rem;
|
|
358
|
+
}
|
|
359
|
+
</style>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* The words half of a band: eyebrow, heading, paragraph, checked list, link.
|
|
4
|
+
*
|
|
5
|
+
* Extracted because a band may need two of them. A split try-it client is two
|
|
6
|
+
* rows — the form, then the samples — and each row wants its own sentence
|
|
7
|
+
* saying what the reader is looking at; without this they were one block of
|
|
8
|
+
* markup copied twice, which is how the second copy stops matching the first.
|
|
9
|
+
*
|
|
10
|
+
* THE HEADING LEVEL IS A PROP, not a fixed `h2`. The band's own title is the
|
|
11
|
+
* `h2` its `aria-labelledby` points at; a second row inside the same section is
|
|
12
|
+
* a part of it, and an `h2` there would announce a section that does not exist.
|
|
13
|
+
*/
|
|
14
|
+
withDefaults(
|
|
15
|
+
defineProps<{
|
|
16
|
+
prose: DuxtResolved<DuxtShowcaseProse>;
|
|
17
|
+
/** The DOM id the band's section is labelled by. Only the `h2` needs one. */
|
|
18
|
+
headingId?: string;
|
|
19
|
+
level?: 2 | 3;
|
|
20
|
+
}>(),
|
|
21
|
+
{ level: 2 }
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const localeLink = useDuxtLink();
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<div>
|
|
29
|
+
<p
|
|
30
|
+
v-if="prose.badge"
|
|
31
|
+
class="mb-4 flex items-center gap-2 text-sm font-medium text-primary"
|
|
32
|
+
>
|
|
33
|
+
<span
|
|
34
|
+
v-if="prose.icon"
|
|
35
|
+
class="flex size-7 items-center justify-center rounded-lg border bg-muted/50"
|
|
36
|
+
>
|
|
37
|
+
<Icon :name="prose.icon" class="size-4" />
|
|
38
|
+
</span>
|
|
39
|
+
{{ prose.badge }}
|
|
40
|
+
</p>
|
|
41
|
+
|
|
42
|
+
<component
|
|
43
|
+
:is="`h${level}`"
|
|
44
|
+
:id="headingId"
|
|
45
|
+
class="font-semibold tracking-tight text-balance"
|
|
46
|
+
:class="level === 2 ? 'text-2xl sm:text-3xl' : 'text-xl sm:text-2xl'"
|
|
47
|
+
>
|
|
48
|
+
{{ prose.title }}
|
|
49
|
+
</component>
|
|
50
|
+
|
|
51
|
+
<p
|
|
52
|
+
v-if="prose.description"
|
|
53
|
+
class="mt-4 text-base text-muted-foreground text-pretty"
|
|
54
|
+
>
|
|
55
|
+
{{ prose.description }}
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
<ul v-if="prose.bullets?.length" class="mt-6 space-y-3">
|
|
59
|
+
<li
|
|
60
|
+
v-for="bullet in prose.bullets"
|
|
61
|
+
:key="bullet.label"
|
|
62
|
+
class="flex items-start gap-3 text-sm"
|
|
63
|
+
>
|
|
64
|
+
<Icon
|
|
65
|
+
:name="bullet.icon ?? 'lucide:check'"
|
|
66
|
+
class="mt-0.5 size-4 shrink-0 text-primary"
|
|
67
|
+
/>
|
|
68
|
+
<span class="text-pretty">{{ bullet.label }}</span>
|
|
69
|
+
</li>
|
|
70
|
+
</ul>
|
|
71
|
+
|
|
72
|
+
<UiButton v-if="prose.action" as-child variant="link" class="mt-6 px-0">
|
|
73
|
+
<NuxtLink
|
|
74
|
+
:to="localeLink(prose.action.to ?? '/')"
|
|
75
|
+
:target="prose.action.external ? '_blank' : undefined"
|
|
76
|
+
:rel="prose.action.external ? 'noopener' : undefined"
|
|
77
|
+
>
|
|
78
|
+
{{ prose.action.label }}
|
|
79
|
+
<Icon
|
|
80
|
+
:name="
|
|
81
|
+
prose.action.external
|
|
82
|
+
? 'lucide:arrow-up-right'
|
|
83
|
+
: 'lucide:arrow-right'
|
|
84
|
+
"
|
|
85
|
+
class="size-4"
|
|
86
|
+
/>
|
|
87
|
+
</NuxtLink>
|
|
88
|
+
</UiButton>
|
|
89
|
+
</div>
|
|
90
|
+
</template>
|