@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,365 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DuxtOpenApiSchema } from '../../openapi-model';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* One schema, drawn as the tree it is — and recursive, because schemas are.
|
|
6
|
+
*
|
|
7
|
+
* The three things that make this more than an `<li>` per property:
|
|
8
|
+
*
|
|
9
|
+
* - **A `$ref` keeps its name.** The parser followed the reference and kept
|
|
10
|
+
* what it was followed through, so `Pet` is shown as `Pet` AND expanded. A
|
|
11
|
+
* reference drawn only as its expansion loses the vocabulary the API's own
|
|
12
|
+
* documentation uses everywhere else.
|
|
13
|
+
* - **A cycle stops.** `Pet.friends: Pet[]` is ordinary, and the parser marked
|
|
14
|
+
* the second `Pet` as circular rather than expanding it; this draws that as
|
|
15
|
+
* the reference it is instead of recursing until the browser gives out.
|
|
16
|
+
* - **Composition is drawn as composition.** `oneOf` and `anyOf` are branches a
|
|
17
|
+
* reader has to choose between, so they are labelled and nested rather than
|
|
18
|
+
* flattened into one list of properties that no single response ever has.
|
|
19
|
+
*
|
|
20
|
+
* `DEPTH` is a floor under all of that: a deeply self-referential document
|
|
21
|
+
* would otherwise render until the tab stops responding, and a truncated tree
|
|
22
|
+
* that says it is truncated is the honest answer.
|
|
23
|
+
*/
|
|
24
|
+
const props = withDefaults(
|
|
25
|
+
defineProps<{
|
|
26
|
+
schema?: DuxtOpenApiSchema;
|
|
27
|
+
/** The property name this schema sits under, when it has one. */
|
|
28
|
+
name?: string;
|
|
29
|
+
required?: boolean;
|
|
30
|
+
/** Which way the body this schema describes travels — see `excluded`. */
|
|
31
|
+
direction: DuxtOpenApiDirection;
|
|
32
|
+
depth?: number;
|
|
33
|
+
}>(),
|
|
34
|
+
{ depth: 0 }
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const DEPTH = 8;
|
|
38
|
+
|
|
39
|
+
const schema = computed(() => props.schema);
|
|
40
|
+
const deep = computed(() => props.depth >= DEPTH);
|
|
41
|
+
|
|
42
|
+
const type = computed(() => openApiTypeLabel(schema.value));
|
|
43
|
+
const constraints = computed(() => openApiConstraints(schema.value));
|
|
44
|
+
|
|
45
|
+
const branches = computed(() =>
|
|
46
|
+
(
|
|
47
|
+
[
|
|
48
|
+
['oneOf', schema.value?.oneOf],
|
|
49
|
+
['anyOf', schema.value?.anyOf],
|
|
50
|
+
['allOf', schema.value?.allOf]
|
|
51
|
+
] as const
|
|
52
|
+
).filter(([, list]) => list?.length)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
/** The array's element schema, drawn under the array rather than beside it. */
|
|
56
|
+
const items = computed(() =>
|
|
57
|
+
schema.value?.types?.includes('array') || schema.value?.items
|
|
58
|
+
? schema.value?.items
|
|
59
|
+
: undefined
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// `true` never reaches here: the parser turns "any further property is
|
|
63
|
+
// allowed" into an empty schema, so the only two answers left are a schema
|
|
64
|
+
// that constrains them and `false`, which forbids them outright.
|
|
65
|
+
const additional = computed(() => {
|
|
66
|
+
const value = schema.value?.additionalProperties;
|
|
67
|
+
|
|
68
|
+
return value === false || value === undefined ? undefined : value;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const nested = computed(
|
|
72
|
+
() =>
|
|
73
|
+
Boolean(schema.value?.properties?.length) ||
|
|
74
|
+
Boolean(branches.value.length) ||
|
|
75
|
+
Boolean(items.value) ||
|
|
76
|
+
Boolean(additional.value) ||
|
|
77
|
+
Boolean(schema.value?.not)
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const { t } = useI18n();
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Type, format, constraints and flags as ONE grey run, `·` between them.
|
|
84
|
+
*
|
|
85
|
+
* They were a line of chips under the name, and a field cost two or three
|
|
86
|
+
* lines: name and type, a red "required", then two boxes saying `min length 1`
|
|
87
|
+
* and `max length 120`. An object with eight fields filled a screen with
|
|
88
|
+
* twenty-four lines of furniture around eight facts. Set as one run —
|
|
89
|
+
* `string · min 1 · max 120` — a field is a line, which is what it is.
|
|
90
|
+
*
|
|
91
|
+
* The enum stays in the run as alternatives (`round | square | other`) rather
|
|
92
|
+
* than as one chip per value: they are one fact about the field, not five.
|
|
93
|
+
*/
|
|
94
|
+
const meta = computed(() => {
|
|
95
|
+
const parts: string[] = [type.value];
|
|
96
|
+
|
|
97
|
+
if (schema.value?.name) parts.push(schema.value.name);
|
|
98
|
+
|
|
99
|
+
for (const constraint of constraints.value) {
|
|
100
|
+
const label = t(`duxt.openapi.constraints.${constraint.key}`);
|
|
101
|
+
|
|
102
|
+
parts.push(
|
|
103
|
+
constraint.value === 'true' ? label : `${label} ${constraint.value}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const options = schema.value?.enum ?? [];
|
|
108
|
+
if (options.length) parts.push(options.map(openApiJson).join(' | '));
|
|
109
|
+
|
|
110
|
+
if (schema.value?.readOnly) parts.push(t('duxt.openapi.readOnly'));
|
|
111
|
+
if (schema.value?.writeOnly) parts.push(t('duxt.openapi.writeOnly'));
|
|
112
|
+
if (schema.value?.deprecated) parts.push(t('duxt.openapi.deprecated'));
|
|
113
|
+
|
|
114
|
+
return parts.filter(Boolean);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* How many fields lie behind this row — the number the chip shows, and the
|
|
119
|
+
* number the open-by-default rule reads.
|
|
120
|
+
*
|
|
121
|
+
* Through an ARRAY, because `items: Widget[]` is a row a reader opens to see a
|
|
122
|
+
* widget: the array itself has no properties and the seven behind it are what
|
|
123
|
+
* the click is worth.
|
|
124
|
+
*/
|
|
125
|
+
const size = computed(() => {
|
|
126
|
+
const inner = items.value ?? schema.value;
|
|
127
|
+
|
|
128
|
+
if (inner?.properties?.length) return inner.properties.length;
|
|
129
|
+
if (branches.value.length) {
|
|
130
|
+
return branches.value.reduce(
|
|
131
|
+
(all, [, list]) => all + (list?.length ?? 0),
|
|
132
|
+
0
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return nested.value ? 1 : 0;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* ONE THRESHOLD FOR TWO RULES: five fields is where a row starts closed, and
|
|
141
|
+
* five is where the count is worth printing. Below it the chip would say
|
|
142
|
+
* something nobody weighed a decision on ("1"), and the row would hide a line
|
|
143
|
+
* and a half.
|
|
144
|
+
*/
|
|
145
|
+
const MANY = 5;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* A row can only close if it HAS a row: the media type's own schema, an array's
|
|
149
|
+
* item and a branch of a `oneOf` arrive without a name, so there is nothing to
|
|
150
|
+
* click and they are always drawn open.
|
|
151
|
+
*/
|
|
152
|
+
const collapsible = computed(
|
|
153
|
+
() =>
|
|
154
|
+
Boolean(props.name) &&
|
|
155
|
+
nested.value &&
|
|
156
|
+
!deep.value &&
|
|
157
|
+
!schema.value?.circular
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const open = computed(() => size.value < MANY);
|
|
161
|
+
</script>
|
|
162
|
+
|
|
163
|
+
<template>
|
|
164
|
+
<!-- `details` only where there is a name to click: everything else is drawn
|
|
165
|
+
open, because a summary nobody can aim at is a summary nobody can open.
|
|
166
|
+
The TAG varies, the row does not — one block of markup either way, so a
|
|
167
|
+
collapsed and an expanded field cannot drift apart. -->
|
|
168
|
+
<component
|
|
169
|
+
:is="collapsible ? 'details' : 'div'"
|
|
170
|
+
:open="collapsible ? open : undefined"
|
|
171
|
+
class="min-w-0"
|
|
172
|
+
>
|
|
173
|
+
<component
|
|
174
|
+
:is="collapsible ? 'summary' : 'div'"
|
|
175
|
+
:class="collapsible ? 'duxt-schema-row' : ''"
|
|
176
|
+
>
|
|
177
|
+
<span class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
|
|
178
|
+
<!-- The mark is lucide's own `plus` and `minus`, one shown per state.
|
|
179
|
+
It was two pseudo-elements before, and at 16 pixels the percentage
|
|
180
|
+
insets that positioned them rounded to different thicknesses — a
|
|
181
|
+
cross with one heavy stroke. An icon set that already draws this
|
|
182
|
+
pair with round caps and one stroke width draws it better than an
|
|
183
|
+
arithmetic of insets ever will.
|
|
184
|
+
|
|
185
|
+
The TILE around it is what says "control" while nothing is being
|
|
186
|
+
hovered, which a bare glyph in grey never did. -->
|
|
187
|
+
<span v-if="collapsible" aria-hidden="true" class="duxt-schema-tile">
|
|
188
|
+
<Icon name="lucide:plus" class="duxt-schema-plus size-2.5" />
|
|
189
|
+
<Icon name="lucide:minus" class="duxt-schema-minus size-2.5" />
|
|
190
|
+
</span>
|
|
191
|
+
|
|
192
|
+
<code v-if="name" class="font-mono text-sm font-medium text-foreground">
|
|
193
|
+
{{ name }}<!-- `required` FOLLOWS THE DIRECTION. A property may be
|
|
194
|
+
`readOnly` and listed under `required`, which is the ordinary way to
|
|
195
|
+
describe a field the server assigns — and OpenAPI then means it is
|
|
196
|
+
required in the response, never in the request. Marking it required
|
|
197
|
+
on a request body asked the reader to invent an id.
|
|
198
|
+
|
|
199
|
+
A STAR, not the word: on a body with eight required fields, eight
|
|
200
|
+
red words are a page that shouts, and a warning colour that appears
|
|
201
|
+
everywhere warns of nothing. The word survives for a screen reader,
|
|
202
|
+
which cannot see that the star is red. --><span
|
|
203
|
+
v-if="required && !excluded(schema ?? {}, direction)"
|
|
204
|
+
class="font-semibold text-destructive"
|
|
205
|
+
:title="$t('duxt.openapi.required')"
|
|
206
|
+
>*<span class="sr-only">
|
|
207
|
+
{{ $t('duxt.openapi.required') }}</span
|
|
208
|
+
></span
|
|
209
|
+
>
|
|
210
|
+
</code>
|
|
211
|
+
|
|
212
|
+
<!-- One run, `·` between: see `meta`. -->
|
|
213
|
+
<span class="font-mono text-xs text-muted-foreground">
|
|
214
|
+
<template v-for="(part, index) in meta" :key="index"
|
|
215
|
+
><span v-if="index" aria-hidden="true" class="px-1 opacity-50"
|
|
216
|
+
>·</span
|
|
217
|
+
>{{ part }}</template
|
|
218
|
+
>
|
|
219
|
+
</span>
|
|
220
|
+
|
|
221
|
+
<!-- Only past the threshold, and it is the SAME threshold that closed
|
|
222
|
+
the row: a chip therefore always means "this is bigger than you
|
|
223
|
+
read in passing" rather than "this opens", which the tile already
|
|
224
|
+
said. -->
|
|
225
|
+
<span
|
|
226
|
+
v-if="collapsible && size >= MANY"
|
|
227
|
+
class="ml-auto rounded-md bg-muted px-1.5 font-mono text-[0.6875rem] text-muted-foreground tabular-nums"
|
|
228
|
+
:aria-label="$t('duxt.openapi.fields', { count: size })"
|
|
229
|
+
>
|
|
230
|
+
{{ size }}
|
|
231
|
+
</span>
|
|
232
|
+
</span>
|
|
233
|
+
|
|
234
|
+
<span v-if="schema?.title" class="mt-1 block text-sm font-medium">
|
|
235
|
+
{{ schema.title }}
|
|
236
|
+
</span>
|
|
237
|
+
|
|
238
|
+
<span
|
|
239
|
+
v-if="schema?.description"
|
|
240
|
+
class="mt-1 block text-sm text-muted-foreground"
|
|
241
|
+
>
|
|
242
|
+
{{ schema.description }}
|
|
243
|
+
</span>
|
|
244
|
+
|
|
245
|
+
<!-- A reference this build could not follow. Said out loud rather than
|
|
246
|
+
drawn as an empty object, which is what it would otherwise look
|
|
247
|
+
like. -->
|
|
248
|
+
<span
|
|
249
|
+
v-if="schema?.external"
|
|
250
|
+
class="mt-1 block text-sm text-muted-foreground"
|
|
251
|
+
>
|
|
252
|
+
{{ $t('duxt.openapi.externalRef') }}
|
|
253
|
+
<code class="font-mono text-xs">{{ schema.ref }}</code>
|
|
254
|
+
</span>
|
|
255
|
+
|
|
256
|
+
<span
|
|
257
|
+
v-else-if="schema?.circular"
|
|
258
|
+
class="mt-1 block text-sm text-muted-foreground"
|
|
259
|
+
>
|
|
260
|
+
{{ $t('duxt.openapi.circular') }}
|
|
261
|
+
</span>
|
|
262
|
+
</component>
|
|
263
|
+
|
|
264
|
+
<p v-if="nested && deep" class="mt-2 text-sm text-muted-foreground">
|
|
265
|
+
{{ $t('duxt.openapi.truncated') }}
|
|
266
|
+
</p>
|
|
267
|
+
|
|
268
|
+
<template v-else-if="!schema?.circular">
|
|
269
|
+
<ul
|
|
270
|
+
v-if="schema?.properties?.length"
|
|
271
|
+
class="mt-3 space-y-3 border-l pl-4"
|
|
272
|
+
>
|
|
273
|
+
<li v-for="property in schema.properties" :key="property.name">
|
|
274
|
+
<DuxtOpenApiSchema
|
|
275
|
+
:schema="property.schema"
|
|
276
|
+
:name="property.name"
|
|
277
|
+
:required="property.required"
|
|
278
|
+
:direction="direction"
|
|
279
|
+
:depth="depth + 1"
|
|
280
|
+
/>
|
|
281
|
+
</li>
|
|
282
|
+
</ul>
|
|
283
|
+
|
|
284
|
+
<div v-if="items" class="mt-3 border-l pl-4">
|
|
285
|
+
<p class="mb-1 text-xs font-medium text-muted-foreground">
|
|
286
|
+
{{ $t('duxt.openapi.items') }}
|
|
287
|
+
</p>
|
|
288
|
+
<DuxtOpenApiSchema
|
|
289
|
+
:schema="items"
|
|
290
|
+
:direction="direction"
|
|
291
|
+
:depth="depth + 1"
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
<div v-if="additional" class="mt-3 border-l pl-4">
|
|
296
|
+
<p class="mb-1 text-xs font-medium text-muted-foreground">
|
|
297
|
+
{{ $t('duxt.openapi.additionalProperties') }}
|
|
298
|
+
</p>
|
|
299
|
+
<DuxtOpenApiSchema
|
|
300
|
+
:schema="additional"
|
|
301
|
+
:direction="direction"
|
|
302
|
+
:depth="depth + 1"
|
|
303
|
+
/>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<p
|
|
307
|
+
v-else-if="schema?.additionalProperties === false"
|
|
308
|
+
class="mt-2 text-xs text-muted-foreground"
|
|
309
|
+
>
|
|
310
|
+
{{ $t('duxt.openapi.noAdditionalProperties') }}
|
|
311
|
+
</p>
|
|
312
|
+
|
|
313
|
+
<div
|
|
314
|
+
v-for="[kind, list] in branches"
|
|
315
|
+
:key="kind"
|
|
316
|
+
class="mt-3 border-l pl-4"
|
|
317
|
+
>
|
|
318
|
+
<p class="mb-1 text-xs font-medium text-muted-foreground">
|
|
319
|
+
{{ $t(`duxt.openapi.composition.${kind}`) }}
|
|
320
|
+
</p>
|
|
321
|
+
<ul class="space-y-3">
|
|
322
|
+
<li v-for="(branch, index) in list" :key="index">
|
|
323
|
+
<DuxtOpenApiSchema
|
|
324
|
+
:schema="branch"
|
|
325
|
+
:direction="direction"
|
|
326
|
+
:depth="depth + 1"
|
|
327
|
+
/>
|
|
328
|
+
</li>
|
|
329
|
+
</ul>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<div v-if="schema?.not" class="mt-3 border-l pl-4">
|
|
333
|
+
<p class="mb-1 text-xs font-medium text-muted-foreground">
|
|
334
|
+
{{ $t('duxt.openapi.composition.not') }}
|
|
335
|
+
</p>
|
|
336
|
+
<DuxtOpenApiSchema
|
|
337
|
+
:schema="schema.not"
|
|
338
|
+
:direction="direction"
|
|
339
|
+
:depth="depth + 1"
|
|
340
|
+
/>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<p v-if="schema?.externalDocs" class="mt-2 text-sm">
|
|
344
|
+
<a
|
|
345
|
+
:href="schema.externalDocs.url"
|
|
346
|
+
rel="noopener noreferrer"
|
|
347
|
+
target="_blank"
|
|
348
|
+
class="text-primary underline underline-offset-4"
|
|
349
|
+
>
|
|
350
|
+
{{ schema.externalDocs.description ?? $t('duxt.openapi.moreInfo') }}
|
|
351
|
+
</a>
|
|
352
|
+
</p>
|
|
353
|
+
|
|
354
|
+
<!-- The discriminator names the property a reader picks a branch by, and
|
|
355
|
+
without it a `oneOf` of ten shapes is a guessing game. -->
|
|
356
|
+
<p
|
|
357
|
+
v-if="schema?.discriminator"
|
|
358
|
+
class="mt-2 text-xs text-muted-foreground"
|
|
359
|
+
>
|
|
360
|
+
{{ $t('duxt.openapi.discriminator') }}
|
|
361
|
+
<code class="font-mono">{{ schema.discriminator.propertyName }}</code>
|
|
362
|
+
</p>
|
|
363
|
+
</template>
|
|
364
|
+
</component>
|
|
365
|
+
</template>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DuxtOpenApiSecurity } from '../../openapi-model';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What a request has to prove before the endpoint answers it.
|
|
6
|
+
*
|
|
7
|
+
* The two levels of `security` mean different things and are drawn as such: the
|
|
8
|
+
* outer list is ALTERNATIVES — any one of them suffices — and every scheme
|
|
9
|
+
* inside one entry is required together. Flattening them into a single list is
|
|
10
|
+
* the mistake that tells a reader to send an API key AND an OAuth token when
|
|
11
|
+
* either would have done.
|
|
12
|
+
*
|
|
13
|
+
* An EMPTY outer list is a statement, not an absence: the operation opted out
|
|
14
|
+
* of the document's own requirement, and saying "no authentication" out loud is
|
|
15
|
+
* the answer a reader came for.
|
|
16
|
+
*/
|
|
17
|
+
const props = defineProps<{ security?: DuxtOpenApiSecurity }>();
|
|
18
|
+
|
|
19
|
+
const alternatives = computed(() => props.security ?? []);
|
|
20
|
+
const open = computed(() => props.security?.length === 0);
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<section v-if="security" class="mt-8">
|
|
25
|
+
<h2 class="duxt-label">
|
|
26
|
+
{{ $t('duxt.openapi.security') }}
|
|
27
|
+
</h2>
|
|
28
|
+
|
|
29
|
+
<p v-if="open" class="mt-2 text-sm text-muted-foreground">
|
|
30
|
+
{{ $t('duxt.openapi.noAuth') }}
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
<!-- Rows a hairline apart, not a card each. A card says "one object" and
|
|
34
|
+
these are alternatives inside one requirement — three cards down a page
|
|
35
|
+
that also boxes its parameters and its responses is a page made of
|
|
36
|
+
frames rather than of text. -->
|
|
37
|
+
<ul v-else class="mt-3 divide-y divide-border/60 border-t border-border/60">
|
|
38
|
+
<li
|
|
39
|
+
v-for="(alternative, index) in alternatives"
|
|
40
|
+
:key="index"
|
|
41
|
+
class="py-3"
|
|
42
|
+
>
|
|
43
|
+
<p
|
|
44
|
+
v-if="alternatives.length > 1"
|
|
45
|
+
class="mb-2 text-xs text-muted-foreground"
|
|
46
|
+
>
|
|
47
|
+
{{ $t('duxt.openapi.anyOfThese') }}
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<ul class="space-y-3">
|
|
51
|
+
<li v-for="requirement in alternative" :key="requirement.key">
|
|
52
|
+
<div class="flex flex-wrap items-baseline gap-2">
|
|
53
|
+
<code class="font-mono text-sm font-medium">
|
|
54
|
+
{{ requirement.key }}
|
|
55
|
+
</code>
|
|
56
|
+
<span class="text-xs text-muted-foreground">
|
|
57
|
+
{{ requirement.scheme?.type }}
|
|
58
|
+
<template v-if="requirement.scheme?.scheme">
|
|
59
|
+
· {{ requirement.scheme.scheme }}
|
|
60
|
+
</template>
|
|
61
|
+
<template v-if="requirement.scheme?.name">
|
|
62
|
+
· {{ requirement.scheme.name }}
|
|
63
|
+
</template>
|
|
64
|
+
<template v-if="requirement.scheme?.in">
|
|
65
|
+
· {{ $t(`duxt.openapi.in.${requirement.scheme.in}`) }}
|
|
66
|
+
</template>
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<p
|
|
71
|
+
v-if="requirement.scheme?.description"
|
|
72
|
+
class="mt-1 text-sm text-muted-foreground"
|
|
73
|
+
>
|
|
74
|
+
{{ requirement.scheme.description }}
|
|
75
|
+
</p>
|
|
76
|
+
|
|
77
|
+
<div
|
|
78
|
+
v-if="requirement.scopes?.length"
|
|
79
|
+
class="mt-1.5 flex flex-wrap gap-1.5"
|
|
80
|
+
>
|
|
81
|
+
<span
|
|
82
|
+
v-for="scope in requirement.scopes"
|
|
83
|
+
:key="scope"
|
|
84
|
+
class="rounded bg-muted px-1.5 py-0.5 font-mono text-[0.6875rem]"
|
|
85
|
+
>
|
|
86
|
+
{{ scope }}
|
|
87
|
+
</span>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- The flows carry the URLs a reader has to visit to get a token
|
|
91
|
+
at all, which is the whole of what an OAuth section is for. -->
|
|
92
|
+
<dl
|
|
93
|
+
v-if="requirement.scheme?.flows?.length"
|
|
94
|
+
class="mt-2 space-y-1 text-xs text-muted-foreground"
|
|
95
|
+
>
|
|
96
|
+
<div
|
|
97
|
+
v-for="flow in requirement.scheme.flows"
|
|
98
|
+
:key="flow.name"
|
|
99
|
+
class="flex flex-wrap gap-x-2"
|
|
100
|
+
>
|
|
101
|
+
<dt class="font-medium">{{ flow.name }}</dt>
|
|
102
|
+
<dd v-if="flow.authorizationUrl" class="font-mono break-all">
|
|
103
|
+
{{ flow.authorizationUrl }}
|
|
104
|
+
</dd>
|
|
105
|
+
<dd v-if="flow.tokenUrl" class="font-mono break-all">
|
|
106
|
+
{{ flow.tokenUrl }}
|
|
107
|
+
</dd>
|
|
108
|
+
</div>
|
|
109
|
+
</dl>
|
|
110
|
+
</li>
|
|
111
|
+
</ul>
|
|
112
|
+
</li>
|
|
113
|
+
</ul>
|
|
114
|
+
</section>
|
|
115
|
+
</template>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* "Was this page helpful?"
|
|
4
|
+
*
|
|
5
|
+
* A SLOT WITH AN EVENT AND NO BACKEND, and that is the whole design. The layer
|
|
6
|
+
* stores nothing and sends nothing: it has no business knowing where a
|
|
7
|
+
* consumer's analytics live, and a documentation theme that phones home by
|
|
8
|
+
* default is not one to publish. The answer lives in this component's own state
|
|
9
|
+
* for the length of the visit and nowhere else.
|
|
10
|
+
*
|
|
11
|
+
* A consumer that wants the answer kept listens for it:
|
|
12
|
+
*
|
|
13
|
+
* <DuxtPageFeedback @feedback="(helpful) => track(helpful)" />
|
|
14
|
+
*/
|
|
15
|
+
const emit = defineEmits<{ feedback: [helpful: boolean] }>();
|
|
16
|
+
|
|
17
|
+
const answered = ref<boolean | undefined>();
|
|
18
|
+
|
|
19
|
+
// A new page is a new question. Without this the answer given on one page
|
|
20
|
+
// follows the reader to the next.
|
|
21
|
+
const route = useRoute();
|
|
22
|
+
watch(
|
|
23
|
+
() => route.fullPath,
|
|
24
|
+
() => (answered.value = undefined)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
function answer(helpful: boolean) {
|
|
28
|
+
answered.value = helpful;
|
|
29
|
+
emit('feedback', helpful);
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<slot :answered="answered" :answer="answer">
|
|
35
|
+
<div
|
|
36
|
+
class="mt-10 flex flex-wrap items-center gap-3 rounded-lg border px-4 py-3 text-sm"
|
|
37
|
+
>
|
|
38
|
+
<span>{{ $t('duxt.page.helpful.question') }}</span>
|
|
39
|
+
|
|
40
|
+
<template v-if="answered === undefined">
|
|
41
|
+
<UiButton size="sm" variant="outline" @click="answer(true)">
|
|
42
|
+
<Icon name="lucide:thumbs-up" class="size-3.5" />
|
|
43
|
+
{{ $t('duxt.page.helpful.yes') }}
|
|
44
|
+
</UiButton>
|
|
45
|
+
<UiButton size="sm" variant="outline" @click="answer(false)">
|
|
46
|
+
<Icon name="lucide:thumbs-down" class="size-3.5" />
|
|
47
|
+
{{ $t('duxt.page.helpful.no') }}
|
|
48
|
+
</UiButton>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<span v-else role="status" class="text-muted-foreground">
|
|
52
|
+
{{ $t('duxt.page.helpful.thanks') }}
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
55
|
+
</slot>
|
|
56
|
+
</template>
|