@kirchdev/duxt 0.3.4 → 0.3.5-dev-main.43-3310f4b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/README.md +24 -9
  2. package/app/app.vue +17 -11
  3. package/app/assets/css/duxt.css +47 -6
  4. package/app/components/DuxtAnnouncements.vue +117 -0
  5. package/app/components/DuxtBrunoEntries.vue +62 -0
  6. package/app/components/DuxtCodeBlock.vue +18 -34
  7. package/app/components/DuxtCodeToolbar.vue +14 -0
  8. package/app/components/DuxtCopyButton.vue +33 -0
  9. package/app/components/DuxtCopyPage.vue +32 -30
  10. package/app/components/DuxtFooter.vue +39 -8
  11. package/app/components/DuxtHeader.vue +107 -48
  12. package/app/components/DuxtLandingProse.vue +1 -1
  13. package/app/components/DuxtLinkLabel.vue +36 -0
  14. package/app/components/DuxtLiveWindow.vue +8 -8
  15. package/app/components/DuxtLocale.vue +10 -1
  16. package/app/components/DuxtNavigation.vue +2 -2
  17. package/app/components/DuxtOpenApiClient.vue +285 -90
  18. package/app/components/DuxtOpenApiMedia.vue +30 -18
  19. package/app/components/DuxtOpenApiSchema.vue +10 -7
  20. package/app/components/DuxtPageFeedback.vue +7 -0
  21. package/app/components/DuxtPageInfo.vue +15 -12
  22. package/app/components/DuxtPageNav.vue +41 -27
  23. package/app/components/DuxtPreviewSkeleton.vue +4 -4
  24. package/app/components/DuxtProgress.vue +2 -14
  25. package/app/components/DuxtSearch.vue +230 -90
  26. package/app/components/DuxtSearchInput.vue +70 -0
  27. package/app/components/DuxtSections.vue +11 -1
  28. package/app/components/DuxtSegmented.vue +64 -0
  29. package/app/components/DuxtShortcuts.vue +16 -11
  30. package/app/components/DuxtShortcutsTrigger.vue +49 -0
  31. package/app/components/DuxtSkipLink.vue +1 -1
  32. package/app/components/DuxtToc.vue +16 -10
  33. package/app/components/DuxtTranslationBanner.vue +1 -1
  34. package/app/components/DuxtVersion.vue +53 -12
  35. package/app/components/DuxtVersionBanner.vue +5 -5
  36. package/app/components/content/AccordionItem.vue +2 -2
  37. package/app/components/content/BrunoOverview.vue +260 -0
  38. package/app/components/content/BrunoRequest.vue +141 -0
  39. package/app/components/content/BrunoRequests.vue +104 -0
  40. package/app/components/content/Callout.vue +5 -5
  41. package/app/components/content/ChangelogReleases.vue +161 -40
  42. package/app/components/content/CodeGroup.vue +41 -37
  43. package/app/components/content/Field.vue +1 -1
  44. package/app/components/content/FileTree.vue +1 -1
  45. package/app/components/content/OpenApiOperation.vue +4 -4
  46. package/app/components/content/OpenApiOperations.vue +1 -1
  47. package/app/components/content/OpenApiOverview.vue +1 -1
  48. package/app/components/content/PackageManagers.vue +42 -58
  49. package/app/components/content/PageCards.vue +7 -4
  50. package/app/components/content/Preview.vue +15 -39
  51. package/app/components/content/ProseImg.vue +596 -49
  52. package/app/components/content/Since.vue +1 -1
  53. package/app/components/content/Steps.vue +6 -4
  54. package/app/components/content/TabGroup.vue +13 -12
  55. package/app/components/ui/breadcrumb/BreadcrumbSeparator.vue +1 -1
  56. package/app/components/ui/command/CommandShortcut.vue +1 -1
  57. package/app/components/ui/dialog/DialogContent.vue +2 -2
  58. package/app/components/ui/dialog/DialogHeader.vue +1 -1
  59. package/app/components/ui/dialog/DialogScrollContent.vue +1 -1
  60. package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +2 -2
  61. package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
  62. package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +1 -1
  63. package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +2 -2
  64. package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +1 -1
  65. package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +2 -2
  66. package/app/components/ui/navigation-menu/NavigationMenuContent.vue +1 -1
  67. package/app/components/ui/navigation-menu/NavigationMenuTrigger.vue +1 -1
  68. package/app/components/ui/navigation-menu/NavigationMenuViewport.vue +1 -1
  69. package/app/components/ui/scroll-area/ScrollBar.vue +1 -1
  70. package/app/components/ui/select/SelectItem.vue +2 -2
  71. package/app/components/ui/sheet/SheetContent.vue +29 -3
  72. package/app/components/ui/sidebar/Sidebar.vue +28 -4
  73. package/app/components/ui/sidebar/SidebarGroupAction.vue +1 -1
  74. package/app/components/ui/sidebar/SidebarInset.vue +1 -1
  75. package/app/components/ui/sidebar/SidebarMenuAction.vue +1 -1
  76. package/app/components/ui/sidebar/SidebarMenuBadge.vue +1 -1
  77. package/app/components/ui/sidebar/SidebarMenuSub.vue +1 -1
  78. package/app/components/ui/sidebar/SidebarMenuSubButton.vue +1 -1
  79. package/app/components/ui/sidebar/SidebarProvider.vue +7 -16
  80. package/app/components/ui/sidebar/index.ts +10 -2
  81. package/app/components/ui/sidebar/utils.ts +0 -1
  82. package/app/components/ui/sonner/Sonner.vue +18 -0
  83. package/app/components/ui/tabs/TabsList.vue +13 -3
  84. package/app/components/ui/tabs/TabsTrigger.vue +24 -3
  85. package/app/components/ui/tooltip/TooltipContent.vue +2 -2
  86. package/app/composables/useActiveHeading.ts +3 -25
  87. package/app/composables/useDuxtAnalytics.ts +50 -0
  88. package/app/composables/useDuxtAnimatedHeight.ts +8 -38
  89. package/app/composables/useDuxtAnnouncements.ts +153 -0
  90. package/app/composables/useDuxtCopy.ts +51 -0
  91. package/app/composables/useDuxtDirection.ts +23 -0
  92. package/app/composables/useDuxtNavigation.ts +28 -8
  93. package/app/composables/useDuxtPath.ts +3 -5
  94. package/app/composables/useDuxtSearch.ts +87 -35
  95. package/app/composables/useDuxtSection.ts +15 -1
  96. package/app/composables/useDuxtSectionRow.ts +6 -5
  97. package/app/composables/useDuxtShortcuts.ts +319 -20
  98. package/app/composables/useDuxtVersion.ts +57 -6
  99. package/app/composables/useDuxtViewportMeasure.ts +37 -0
  100. package/app/composables/useFuzzySearch.ts +9 -4
  101. package/app/composables/useRecentPages.ts +14 -17
  102. package/app/error.vue +112 -74
  103. package/app/layouts/changelog.vue +9 -1
  104. package/app/layouts/default.vue +9 -0
  105. package/app/layouts/docs.vue +9 -1
  106. package/app/layouts/reference.vue +9 -1
  107. package/app/middleware/duxt-section-layout.global.ts +1 -3
  108. package/app/pages/[...slug].vue +146 -35
  109. package/app/pages/index.vue +15 -23
  110. package/app/types/duxt.d.ts +312 -4
  111. package/app/utils/analytics.ts +91 -0
  112. package/app/utils/announcements.ts +205 -0
  113. package/app/utils/changelog.ts +0 -28
  114. package/app/utils/code-highlight.ts +3 -1
  115. package/app/utils/contributor-avatar.ts +27 -0
  116. package/app/utils/direction.ts +43 -0
  117. package/app/utils/duxt-config.ts +43 -0
  118. package/app/utils/duxt-text.ts +10 -0
  119. package/app/utils/format-date.ts +28 -0
  120. package/app/utils/generated-sections.ts +68 -12
  121. package/app/utils/generated-toc.ts +14 -7
  122. package/app/utils/locale-path.ts +43 -0
  123. package/app/utils/navigation-tree.ts +51 -1
  124. package/app/utils/nearest-page.ts +8 -0
  125. package/app/utils/openapi.ts +16 -11
  126. package/app/utils/page-controls.ts +215 -0
  127. package/app/utils/pill.ts +61 -0
  128. package/app/utils/prose-image.ts +128 -0
  129. package/app/utils/search-context.ts +110 -0
  130. package/app/utils/search-display.ts +248 -0
  131. package/app/utils/section-scope.ts +65 -4
  132. package/app/utils/stored-json.ts +31 -0
  133. package/app/utils/version-choices.ts +6 -13
  134. package/app/utils/version-paths.ts +21 -0
  135. package/bin/duxt-cache-key.mjs +61 -0
  136. package/bin/duxt-og-cache.mjs +58 -0
  137. package/bin/{duxt-report.mjs → duxt.mjs} +13 -7
  138. package/bruno-model.ts +169 -0
  139. package/bruno-parse.ts +656 -0
  140. package/bruno-zip.ts +195 -0
  141. package/cli.ts +240 -0
  142. package/duxt-app-config.ts +18 -1
  143. package/frontmatter.ts +49 -0
  144. package/git-contributors.ts +322 -0
  145. package/i18n/locales/de/duxt/announcement.json +7 -0
  146. package/i18n/locales/de/duxt/bruno.json +25 -0
  147. package/i18n/locales/de/duxt/changelog.json +1 -1
  148. package/i18n/locales/de/duxt/code.json +2 -1
  149. package/i18n/locales/de/duxt/page.json +9 -0
  150. package/i18n/locales/de/duxt/shortcuts.json +1 -0
  151. package/i18n/locales/de/duxt/theme.json +2 -1
  152. package/i18n/locales/en/duxt/announcement.json +7 -0
  153. package/i18n/locales/en/duxt/bruno.json +25 -0
  154. package/i18n/locales/en/duxt/changelog.json +1 -1
  155. package/i18n/locales/en/duxt/code.json +2 -1
  156. package/i18n/locales/en/duxt/page.json +9 -0
  157. package/i18n/locales/en/duxt/shortcuts.json +1 -0
  158. package/i18n/locales/en/duxt/theme.json +2 -1
  159. package/i18n/locales/es/duxt/announcement.json +7 -0
  160. package/i18n/locales/es/duxt/bruno.json +25 -0
  161. package/i18n/locales/es/duxt/changelog.json +1 -1
  162. package/i18n/locales/es/duxt/code.json +2 -1
  163. package/i18n/locales/es/duxt/page.json +9 -0
  164. package/i18n/locales/es/duxt/shortcuts.json +1 -0
  165. package/i18n/locales/es/duxt/theme.json +2 -1
  166. package/i18n/locales/fr/duxt/announcement.json +7 -0
  167. package/i18n/locales/fr/duxt/bruno.json +25 -0
  168. package/i18n/locales/fr/duxt/changelog.json +1 -1
  169. package/i18n/locales/fr/duxt/code.json +2 -1
  170. package/i18n/locales/fr/duxt/page.json +9 -0
  171. package/i18n/locales/fr/duxt/shortcuts.json +1 -0
  172. package/i18n/locales/fr/duxt/theme.json +2 -1
  173. package/i18n/locales/pt/duxt/announcement.json +7 -0
  174. package/i18n/locales/pt/duxt/bruno.json +25 -0
  175. package/i18n/locales/pt/duxt/changelog.json +1 -1
  176. package/i18n/locales/pt/duxt/code.json +2 -1
  177. package/i18n/locales/pt/duxt/page.json +9 -0
  178. package/i18n/locales/pt/duxt/shortcuts.json +1 -0
  179. package/i18n/locales/pt/duxt/theme.json +2 -1
  180. package/modules/bruno.ts +118 -0
  181. package/modules/config.ts +26 -0
  182. package/modules/devtools.ts +2 -3
  183. package/modules/git-meta.ts +16 -47
  184. package/modules/search-records.ts +132 -0
  185. package/nuxt.config.ts +81 -7
  186. package/og-image-cache.ts +742 -0
  187. package/package.json +41 -9
  188. package/public/devtools/checks.html +2 -2
  189. package/public/devtools/config.html +59 -4
  190. package/public/devtools/pages.html +8 -1
  191. package/public/devtools/paths.html +1 -1
  192. package/public/devtools/sources.html +2 -2
  193. package/public/devtools/versions.html +3 -0
  194. package/report.ts +28 -19
  195. package/search-records.ts +380 -0
  196. package/section-input.ts +142 -0
  197. package/section-markdown.ts +67 -0
  198. package/section-reports.ts +10 -4
  199. package/sections-bruno.ts +571 -0
  200. package/sections-changelog.ts +76 -43
  201. package/sections-openapi.ts +12 -83
  202. package/sections-resolve.ts +174 -8
  203. package/sections.ts +33 -10
  204. package/server/devtools/shell.ts +1 -1
  205. package/server/mcp/tools/list-pages.ts +77 -36
  206. package/server/mcp/tools/list-versions.ts +84 -0
  207. package/server/mcp/tools/read-page.ts +80 -26
  208. package/server/mcp/tools/search-docs.ts +87 -53
  209. package/server/middleware/raw-markdown.ts +1 -1
  210. package/server/routes/llms-full.txt.get.ts +6 -8
  211. package/server/routes/llms.txt.get.ts +1 -1
  212. package/server/utils/duxt-server-text.ts +0 -13
  213. package/server/utils/llms-pages.ts +24 -9
  214. package/server/utils/mcp-docs.ts +577 -0
  215. package/sources-cache.ts +376 -0
  216. package/sources-git.ts +186 -31
  217. package/sources-resolve.ts +59 -1
  218. package/sources.ts +122 -13
  219. package/tfplugindocs.ts +158 -0
@@ -95,23 +95,21 @@ const active = computed<DuxtPackageManager>({
95
95
  stored.value = value;
96
96
  }
97
97
  });
98
- const copied = ref(false);
99
- const notify = useDuxtToast();
100
- const { t } = useI18n();
98
+ const { copied, copy: copyText } = useDuxtCopy();
99
+ const analytics = useDuxtAnalytics();
101
100
 
102
101
  async function copy() {
103
- try {
104
- await navigator.clipboard.writeText(commands.value[active.value]!);
105
- copied.value = true;
106
- notify.success(t('duxt.code.copiedToast'));
107
- setTimeout(() => (copied.value = false), 2000);
108
- } catch {
109
- notify.error(
110
- 'Could not copy',
111
- 'The clipboard is unavailable in this context.'
112
- );
113
- // Clipboard is unavailable over plain HTTP; a failed copy stays silent.
114
- }
102
+ if (!(await copyText(commands.value[active.value]))) return;
103
+
104
+ // The manager, not the command: which of the four a site's readers reach
105
+ // for is the question this block can answer, and the command itself is
106
+ // already on the page for anyone who wants to know what was copied.
107
+ analytics.track({
108
+ name: 'copy',
109
+ kind: 'package-manager',
110
+ manager: active.value,
111
+ language: 'bash'
112
+ });
115
113
  }
116
114
  </script>
117
115
 
@@ -124,53 +122,39 @@ async function copy() {
124
122
  v-if="managers.length"
125
123
  class="duxt-code my-6 overflow-hidden rounded-lg border bg-card"
126
124
  >
127
- <div
128
- class="flex min-h-11 items-center gap-1 border-b bg-muted/40 px-2 py-1.5"
129
- >
130
- <button
131
- v-for="manager in managers"
132
- :key="manager"
133
- type="button"
134
- class="flex cursor-pointer items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium transition-colors"
135
- :class="
136
- active === manager
137
- ? 'bg-background text-foreground shadow-sm'
138
- : 'text-muted-foreground hover:bg-accent hover:text-foreground'
125
+ <DuxtCodeToolbar>
126
+ <DuxtSegmented
127
+ v-model="active"
128
+ :options="
129
+ managers.map((manager) => ({ value: manager, label: manager }))
139
130
  "
140
- @click="active = manager"
141
131
  >
142
- <Icon
143
- :name="
144
- managerIconsLight[manager] ??
145
- managerIcons[manager] ??
146
- 'lucide:terminal'
147
- "
148
- class="size-3.5"
149
- :class="managerIconsLight[manager] ? 'dark:hidden' : ''"
150
- />
151
- <Icon
152
- v-if="managerIconsLight[manager]"
153
- :name="managerIcons[manager]"
154
- class="hidden size-3.5 dark:block"
155
- />
156
- {{ manager }}
157
- </button>
132
+ <template #option="{ option }">
133
+ <Icon
134
+ :name="
135
+ managerIconsLight[option.value] ??
136
+ managerIcons[option.value] ??
137
+ 'lucide:terminal'
138
+ "
139
+ class="size-3.5"
140
+ :class="managerIconsLight[option.value] ? 'dark:hidden' : ''"
141
+ />
142
+ <Icon
143
+ v-if="managerIconsLight[option.value]"
144
+ :name="managerIcons[option.value]"
145
+ class="hidden size-3.5 dark:block"
146
+ />
147
+ {{ option.label }}
148
+ </template>
149
+ </DuxtSegmented>
158
150
 
159
- <UiButton
160
- variant="ghost"
161
- size="icon"
162
- class="ml-auto size-7"
163
- :aria-label="
164
- copied ? $t('duxt.code.copied') : $t('duxt.code.copyCommand')
165
- "
151
+ <DuxtCopyButton
152
+ :copied="copied"
153
+ :label="$t('duxt.code.copyCommand')"
154
+ class="ms-auto"
166
155
  @click="copy"
167
- >
168
- <Icon
169
- :name="copied ? 'lucide:check' : 'lucide:copy'"
170
- class="size-3.5"
171
- />
172
- </UiButton>
173
- </div>
156
+ />
157
+ </DuxtCodeToolbar>
174
158
 
175
159
  <!-- eslint-disable-next-line vue/no-v-html -- Shiki output, built on the
176
160
  server from this component's own prop, never from page content. -->
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import type { ContentNavigationItem } from '@nuxt/content';
3
+ import { navigationCardItems } from '../../utils/navigation-tree';
3
4
  // `::page-cards` on a section's index page: one card per child, taken from the
4
5
  // navigation, so a new page appears here without the index being edited.
5
6
  const props = defineProps<{ path?: string }>();
@@ -11,10 +12,12 @@ const { data: navigation } = await useDuxtNavigation();
11
12
 
12
13
  const base = computed(() => props.path ?? path.value);
13
14
 
14
- const items = computed(() => {
15
- const branch = navigation.value?.find((item) => item.path === base.value);
16
- return branch?.children?.filter((child) => child.path !== base.value) ?? [];
17
- });
15
+ const items = computed(() =>
16
+ navigationCardItems(
17
+ navigation.value?.find((item) => item.path === base.value),
18
+ base.value
19
+ )
20
+ );
18
21
 
19
22
  /** `icon` comes from frontmatter, which Content types as unknown. */
20
23
  const iconOf = (item: ContentNavigationItem) =>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'reka-ui';
2
+ import { TabsContent, TabsRoot } from 'reka-ui';
3
3
 
4
4
  /**
5
5
  * `::preview` — a component rendered as the page renders it, with the MDC that
@@ -68,22 +68,7 @@ const codeMeta = computed(() => {
68
68
  * the fence's header is hidden: it would say `mdc` directly under a tab that
69
69
  * already says `mdc`, and one card does not need naming twice.
70
70
  */
71
- const notify = useDuxtToast();
72
- const copied = ref(false);
73
-
74
- async function copy() {
75
- const code = codeMeta.value.code;
76
- if (!code) return;
77
-
78
- try {
79
- await navigator.clipboard.writeText(code);
80
- copied.value = true;
81
- notify.success(t('duxt.code.copiedToast'));
82
- setTimeout(() => (copied.value = false), 2000);
83
- } catch {
84
- notify.error(t('duxt.page.copyFailed'));
85
- }
86
- }
71
+ const { copied, copy } = useDuxtCopy();
87
72
 
88
73
  const tabs = computed(() => [
89
74
  { value: 'preview', label: t('duxt.code.preview'), icon: 'lucide:eye' },
@@ -114,41 +99,32 @@ const tabs = computed(() => [
114
99
  it: a `tablist` may hold tabs and nothing else, and a button inside one
115
100
  is an `aria-required-children` failure — latent here only because it
116
101
  renders on the source tab, which is not the tab a page loads on. -->
117
- <div
118
- class="flex min-h-11 items-center gap-1 border-b bg-muted/40 px-2 py-1.5"
119
- >
120
- <TabsList
121
- class="flex items-center gap-1"
102
+ <DuxtCodeToolbar>
103
+ <UiTabsList
104
+ variant="bare"
122
105
  :aria-label="$t('duxt.code.previewTabs') as string"
123
106
  >
124
- <TabsTrigger
107
+ <UiTabsTrigger
125
108
  v-for="tab in tabs"
126
109
  :key="tab.value"
127
110
  :value="tab.value"
128
- class="flex cursor-pointer items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm"
111
+ variant="pill"
129
112
  >
130
113
  <Icon :name="tab.icon" class="size-3.5" />
131
114
  {{ tab.label }}
132
- </TabsTrigger>
133
- </TabsList>
115
+ </UiTabsTrigger>
116
+ </UiTabsList>
134
117
 
135
118
  <!-- Right of the tabs, where every other card on the site puts it. Only
136
119
  on the source tab: a copy button beside a rendered example copies
137
120
  something the reader cannot see. -->
138
- <UiButton
121
+ <DuxtCopyButton
139
122
  v-if="active === 'code' && codeMeta.code"
140
- variant="ghost"
141
- size="icon"
142
- class="ml-auto size-7"
143
- :aria-label="copied ? $t('duxt.code.copied') : $t('duxt.code.copy')"
144
- @click="copy"
145
- >
146
- <Icon
147
- :name="copied ? 'lucide:check' : 'lucide:copy'"
148
- class="size-3.5"
149
- />
150
- </UiButton>
151
- </div>
123
+ :copied="copied"
124
+ class="ms-auto"
125
+ @click="copy(codeMeta.code)"
126
+ />
127
+ </DuxtCodeToolbar>
152
128
 
153
129
  <TabsContent value="preview" class="outline-none">
154
130
  <!-- The example on the card's own surface, its outer margins taken off: