@kirchdev/duxt 0.3.4 → 0.3.5-dev-main.44-5337cc1
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/README.md +24 -9
- package/app/app.vue +17 -11
- package/app/assets/css/duxt.css +47 -6
- package/app/components/DuxtAnnouncements.vue +117 -0
- package/app/components/DuxtBrunoEntries.vue +62 -0
- package/app/components/DuxtCodeBlock.vue +18 -34
- package/app/components/DuxtCodeToolbar.vue +14 -0
- package/app/components/DuxtCopyButton.vue +33 -0
- package/app/components/DuxtCopyPage.vue +32 -30
- package/app/components/DuxtFooter.vue +39 -8
- package/app/components/DuxtHeader.vue +107 -48
- package/app/components/DuxtLandingProse.vue +1 -1
- package/app/components/DuxtLinkLabel.vue +36 -0
- package/app/components/DuxtLiveWindow.vue +8 -8
- package/app/components/DuxtLocale.vue +10 -1
- package/app/components/DuxtNavigation.vue +2 -2
- package/app/components/DuxtOpenApiClient.vue +285 -90
- package/app/components/DuxtOpenApiMedia.vue +30 -18
- package/app/components/DuxtOpenApiSchema.vue +10 -7
- package/app/components/DuxtPageFeedback.vue +7 -0
- package/app/components/DuxtPageInfo.vue +15 -12
- package/app/components/DuxtPageNav.vue +41 -27
- package/app/components/DuxtPreviewSkeleton.vue +4 -4
- package/app/components/DuxtProgress.vue +2 -14
- package/app/components/DuxtSearch.vue +230 -90
- package/app/components/DuxtSearchInput.vue +70 -0
- package/app/components/DuxtSections.vue +11 -1
- package/app/components/DuxtSegmented.vue +64 -0
- package/app/components/DuxtShortcuts.vue +16 -11
- package/app/components/DuxtShortcutsTrigger.vue +49 -0
- package/app/components/DuxtSkipLink.vue +1 -1
- package/app/components/DuxtToc.vue +16 -10
- package/app/components/DuxtTranslationBanner.vue +1 -1
- package/app/components/DuxtVersion.vue +53 -12
- package/app/components/DuxtVersionBanner.vue +5 -5
- package/app/components/content/AccordionItem.vue +2 -2
- package/app/components/content/BrunoOverview.vue +260 -0
- package/app/components/content/BrunoRequest.vue +141 -0
- package/app/components/content/BrunoRequests.vue +104 -0
- package/app/components/content/Callout.vue +5 -5
- package/app/components/content/ChangelogReleases.vue +161 -40
- package/app/components/content/CodeGroup.vue +41 -37
- package/app/components/content/Field.vue +1 -1
- package/app/components/content/FileTree.vue +1 -1
- package/app/components/content/OpenApiOperation.vue +4 -4
- package/app/components/content/OpenApiOperations.vue +1 -1
- package/app/components/content/OpenApiOverview.vue +1 -1
- package/app/components/content/PackageManagers.vue +42 -58
- package/app/components/content/PageCards.vue +7 -4
- package/app/components/content/Preview.vue +15 -39
- package/app/components/content/ProseImg.vue +596 -49
- package/app/components/content/Since.vue +1 -1
- package/app/components/content/Steps.vue +6 -4
- package/app/components/content/TabGroup.vue +13 -12
- package/app/components/ui/breadcrumb/BreadcrumbSeparator.vue +1 -1
- package/app/components/ui/command/CommandShortcut.vue +1 -1
- package/app/components/ui/dialog/DialogContent.vue +2 -2
- package/app/components/ui/dialog/DialogHeader.vue +1 -1
- package/app/components/ui/dialog/DialogScrollContent.vue +1 -1
- package/app/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +2 -2
- package/app/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
- package/app/components/ui/dropdown-menu/DropdownMenuLabel.vue +1 -1
- package/app/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +2 -2
- package/app/components/ui/dropdown-menu/DropdownMenuShortcut.vue +1 -1
- package/app/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +2 -2
- package/app/components/ui/navigation-menu/NavigationMenuContent.vue +1 -1
- package/app/components/ui/navigation-menu/NavigationMenuTrigger.vue +1 -1
- package/app/components/ui/navigation-menu/NavigationMenuViewport.vue +1 -1
- package/app/components/ui/scroll-area/ScrollBar.vue +1 -1
- package/app/components/ui/select/SelectItem.vue +2 -2
- package/app/components/ui/sheet/SheetContent.vue +29 -3
- package/app/components/ui/sidebar/Sidebar.vue +28 -4
- package/app/components/ui/sidebar/SidebarGroupAction.vue +1 -1
- package/app/components/ui/sidebar/SidebarInset.vue +1 -1
- package/app/components/ui/sidebar/SidebarMenuAction.vue +1 -1
- package/app/components/ui/sidebar/SidebarMenuBadge.vue +1 -1
- package/app/components/ui/sidebar/SidebarMenuSub.vue +1 -1
- package/app/components/ui/sidebar/SidebarMenuSubButton.vue +1 -1
- package/app/components/ui/sidebar/SidebarProvider.vue +7 -16
- package/app/components/ui/sidebar/index.ts +10 -2
- package/app/components/ui/sidebar/utils.ts +0 -1
- package/app/components/ui/sonner/Sonner.vue +18 -0
- package/app/components/ui/tabs/TabsList.vue +13 -3
- package/app/components/ui/tabs/TabsTrigger.vue +24 -3
- package/app/components/ui/tooltip/TooltipContent.vue +2 -2
- package/app/composables/useActiveHeading.ts +3 -25
- package/app/composables/useDuxtAnalytics.ts +50 -0
- package/app/composables/useDuxtAnimatedHeight.ts +8 -38
- package/app/composables/useDuxtAnnouncements.ts +153 -0
- package/app/composables/useDuxtCopy.ts +51 -0
- package/app/composables/useDuxtDirection.ts +23 -0
- package/app/composables/useDuxtNavigation.ts +28 -8
- package/app/composables/useDuxtPath.ts +3 -5
- package/app/composables/useDuxtSearch.ts +87 -35
- package/app/composables/useDuxtSection.ts +15 -1
- package/app/composables/useDuxtSectionRow.ts +6 -5
- package/app/composables/useDuxtShortcuts.ts +319 -20
- package/app/composables/useDuxtVersion.ts +57 -6
- package/app/composables/useDuxtViewportMeasure.ts +37 -0
- package/app/composables/useFuzzySearch.ts +9 -4
- package/app/composables/useRecentPages.ts +14 -17
- package/app/error.vue +112 -74
- package/app/layouts/changelog.vue +9 -1
- package/app/layouts/default.vue +9 -0
- package/app/layouts/docs.vue +9 -1
- package/app/layouts/reference.vue +9 -1
- package/app/middleware/duxt-section-layout.global.ts +1 -3
- package/app/pages/[...slug].vue +146 -35
- package/app/pages/index.vue +15 -23
- package/app/types/duxt.d.ts +312 -4
- package/app/utils/analytics.ts +91 -0
- package/app/utils/announcements.ts +205 -0
- package/app/utils/changelog.ts +0 -28
- package/app/utils/code-highlight.ts +3 -1
- package/app/utils/contributor-avatar.ts +27 -0
- package/app/utils/direction.ts +43 -0
- package/app/utils/duxt-config.ts +43 -0
- package/app/utils/duxt-text.ts +10 -0
- package/app/utils/format-date.ts +28 -0
- package/app/utils/generated-sections.ts +68 -12
- package/app/utils/generated-toc.ts +14 -7
- package/app/utils/locale-path.ts +43 -0
- package/app/utils/navigation-tree.ts +51 -1
- package/app/utils/nearest-page.ts +8 -0
- package/app/utils/openapi.ts +16 -11
- package/app/utils/page-controls.ts +215 -0
- package/app/utils/pill.ts +61 -0
- package/app/utils/prose-image.ts +128 -0
- package/app/utils/search-context.ts +110 -0
- package/app/utils/search-display.ts +248 -0
- package/app/utils/section-scope.ts +65 -4
- package/app/utils/stored-json.ts +31 -0
- package/app/utils/version-choices.ts +6 -13
- package/app/utils/version-paths.ts +21 -0
- package/bin/duxt-cache-key.mjs +61 -0
- package/bin/duxt-og-cache.mjs +58 -0
- package/bin/{duxt-report.mjs → duxt.mjs} +13 -7
- package/bruno-model.ts +169 -0
- package/bruno-parse.ts +656 -0
- package/bruno-zip.ts +195 -0
- package/cli.ts +240 -0
- package/duxt-app-config.ts +18 -1
- package/frontmatter.ts +49 -0
- package/git-contributors.ts +322 -0
- package/i18n/locales/de/duxt/announcement.json +7 -0
- package/i18n/locales/de/duxt/bruno.json +25 -0
- package/i18n/locales/de/duxt/changelog.json +1 -1
- package/i18n/locales/de/duxt/code.json +2 -1
- package/i18n/locales/de/duxt/page.json +9 -0
- package/i18n/locales/de/duxt/shortcuts.json +1 -0
- package/i18n/locales/de/duxt/theme.json +2 -1
- package/i18n/locales/en/duxt/announcement.json +7 -0
- package/i18n/locales/en/duxt/bruno.json +25 -0
- package/i18n/locales/en/duxt/changelog.json +1 -1
- package/i18n/locales/en/duxt/code.json +2 -1
- package/i18n/locales/en/duxt/page.json +9 -0
- package/i18n/locales/en/duxt/shortcuts.json +1 -0
- package/i18n/locales/en/duxt/theme.json +2 -1
- package/i18n/locales/es/duxt/announcement.json +7 -0
- package/i18n/locales/es/duxt/bruno.json +25 -0
- package/i18n/locales/es/duxt/changelog.json +1 -1
- package/i18n/locales/es/duxt/code.json +2 -1
- package/i18n/locales/es/duxt/page.json +9 -0
- package/i18n/locales/es/duxt/shortcuts.json +1 -0
- package/i18n/locales/es/duxt/theme.json +2 -1
- package/i18n/locales/fr/duxt/announcement.json +7 -0
- package/i18n/locales/fr/duxt/bruno.json +25 -0
- package/i18n/locales/fr/duxt/changelog.json +1 -1
- package/i18n/locales/fr/duxt/code.json +2 -1
- package/i18n/locales/fr/duxt/page.json +9 -0
- package/i18n/locales/fr/duxt/shortcuts.json +1 -0
- package/i18n/locales/fr/duxt/theme.json +2 -1
- package/i18n/locales/pt/duxt/announcement.json +7 -0
- package/i18n/locales/pt/duxt/bruno.json +25 -0
- package/i18n/locales/pt/duxt/changelog.json +1 -1
- package/i18n/locales/pt/duxt/code.json +2 -1
- package/i18n/locales/pt/duxt/page.json +9 -0
- package/i18n/locales/pt/duxt/shortcuts.json +1 -0
- package/i18n/locales/pt/duxt/theme.json +2 -1
- package/modules/bruno.ts +118 -0
- package/modules/config.ts +26 -0
- package/modules/devtools.ts +2 -3
- package/modules/git-meta.ts +16 -47
- package/modules/search-records.ts +132 -0
- package/nuxt.config.ts +81 -7
- package/og-image-cache.ts +742 -0
- package/package.json +42 -10
- package/public/devtools/checks.html +2 -2
- package/public/devtools/config.html +59 -4
- package/public/devtools/pages.html +8 -1
- package/public/devtools/paths.html +1 -1
- package/public/devtools/sources.html +2 -2
- package/public/devtools/versions.html +3 -0
- package/report.ts +28 -19
- package/search-records.ts +380 -0
- package/section-input.ts +142 -0
- package/section-markdown.ts +67 -0
- package/section-reports.ts +10 -4
- package/sections-bruno.ts +571 -0
- package/sections-changelog.ts +76 -43
- package/sections-openapi.ts +12 -83
- package/sections-resolve.ts +174 -8
- package/sections.ts +33 -10
- package/server/devtools/shell.ts +1 -1
- package/server/mcp/tools/list-pages.ts +77 -36
- package/server/mcp/tools/list-versions.ts +84 -0
- package/server/mcp/tools/read-page.ts +80 -26
- package/server/mcp/tools/search-docs.ts +87 -53
- package/server/middleware/raw-markdown.ts +1 -1
- package/server/routes/llms-full.txt.get.ts +6 -8
- package/server/routes/llms.txt.get.ts +1 -1
- package/server/utils/duxt-server-text.ts +0 -13
- package/server/utils/llms-pages.ts +24 -9
- package/server/utils/mcp-docs.ts +577 -0
- package/sources-cache.ts +376 -0
- package/sources-git.ts +186 -31
- package/sources-resolve.ts +59 -1
- package/sources.ts +122 -13
- package/tfplugindocs.ts +158 -0
package/app/pages/[...slug].vue
CHANGED
|
@@ -52,13 +52,32 @@ const { data: found } = await useAsyncData(
|
|
|
52
52
|
if (hit) return { page: hit, from: entry!.locale, asked: locale.value };
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
// `null`, not `undefined`: Nuxt reads a handler that resolves to nothing as
|
|
56
|
+
// one that failed to return (E3006) and repeats the query on the client.
|
|
57
|
+
// No page in any language is a real answer — the 404 below.
|
|
58
|
+
return null;
|
|
56
59
|
},
|
|
57
60
|
{ watch: [collection, path] }
|
|
58
61
|
);
|
|
59
62
|
|
|
60
63
|
const page = computed(() => found.value?.page);
|
|
61
64
|
|
|
65
|
+
/**
|
|
66
|
+
* What THIS page says about the docs shell around it.
|
|
67
|
+
*
|
|
68
|
+
* Eight frontmatter fields, resolved over the site's own switches in one place
|
|
69
|
+
* — see `app/utils/page-controls.ts`. The alternative was eight `v-if`s each
|
|
70
|
+
* reading a different field off `page.value` with its own idea of what an
|
|
71
|
+
* absent value means, which is how `duxt.breadcrumb` ended up being the only
|
|
72
|
+
* one of the eight that existed at all.
|
|
73
|
+
*/
|
|
74
|
+
const controls = computed(() =>
|
|
75
|
+
duxtPageControls(page.value, {
|
|
76
|
+
breadcrumb: duxt?.breadcrumb,
|
|
77
|
+
tocMaxDepth: duxt.toc?.depth
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
|
|
62
81
|
/** True when the reader is being shown a language they did not ask for. */
|
|
63
82
|
const untranslated = computed(() => {
|
|
64
83
|
if (!translates.value || !found.value) return false;
|
|
@@ -76,12 +95,17 @@ if (!page.value) {
|
|
|
76
95
|
throw createError({
|
|
77
96
|
statusCode: 404,
|
|
78
97
|
statusMessage: 'Page not found',
|
|
79
|
-
|
|
98
|
+
// Fatal on the CLIENT only. A client-side navigation needs it to replace
|
|
99
|
+
// the page with the error page; the server renders that page for any
|
|
100
|
+
// error thrown here. Fatal on the server, Nitro files every missing URL
|
|
101
|
+
// as a sensitive failure and logs it with a stack trace — in dev and in
|
|
102
|
+
// production alike, one line per bot probing a path that does not exist.
|
|
103
|
+
fatal: import.meta.client
|
|
80
104
|
});
|
|
81
105
|
}
|
|
82
106
|
|
|
83
107
|
// Feeds the search dialog's empty state.
|
|
84
|
-
const { remember } = useRecentPages();
|
|
108
|
+
const { remember } = useRecentPages(duxt.search?.recentPages);
|
|
85
109
|
onMounted(() =>
|
|
86
110
|
remember({ path: path.value, title: page.value?.title ?? path.value })
|
|
87
111
|
);
|
|
@@ -129,7 +153,22 @@ const owned = computed(() =>
|
|
|
129
153
|
* genuinely have no outline.
|
|
130
154
|
*/
|
|
131
155
|
const generated = computed(() =>
|
|
132
|
-
owned.value
|
|
156
|
+
owned.value && controls.value.toc
|
|
157
|
+
? generatedToc(page.value?.body, controls.value.tocMaxDepth)
|
|
158
|
+
: []
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The written page's outline, cut to the depth the page asked for.
|
|
163
|
+
*
|
|
164
|
+
* Content parses every level down to `h6` — see `content.build.markdown.toc` in
|
|
165
|
+
* `nuxt.config.ts` — so this is where the column is decided rather than at
|
|
166
|
+
* parse time, and `toc: { maxDepth: 5 }` has something to show.
|
|
167
|
+
*/
|
|
168
|
+
const outline = computed(() =>
|
|
169
|
+
controls.value.toc
|
|
170
|
+
? duxtTocLinks(page.value?.body?.toc?.links, controls.value.tocMaxDepth)
|
|
171
|
+
: []
|
|
133
172
|
);
|
|
134
173
|
|
|
135
174
|
/**
|
|
@@ -175,6 +214,21 @@ const aside = computed(
|
|
|
175
214
|
(generated.value.length > 0 || (duxt.aside?.links?.length ?? 0) > 0)
|
|
176
215
|
);
|
|
177
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Is there anything left to put in a written page's right-hand column?
|
|
219
|
+
*
|
|
220
|
+
* It was drawn unconditionally, which was right while nothing could empty it —
|
|
221
|
+
* a page that turns off both the contents and the provenance on a site with no
|
|
222
|
+
* aside links would otherwise reserve fourteen rems for nothing. Page info is
|
|
223
|
+
* on by default, so the ordinary page keeps exactly the column it had.
|
|
224
|
+
*/
|
|
225
|
+
const column = computed(
|
|
226
|
+
() =>
|
|
227
|
+
outline.value.length > 0 ||
|
|
228
|
+
(duxt.aside?.links?.length ?? 0) > 0 ||
|
|
229
|
+
controls.value.pageInfo
|
|
230
|
+
);
|
|
231
|
+
|
|
178
232
|
// The social card. Rendered from the layer's own template unless the consumer
|
|
179
233
|
// ships a component of the same name — see `OgImage/Duxt.satori.vue`.
|
|
180
234
|
//
|
|
@@ -227,6 +281,19 @@ useHead(() => ({
|
|
|
227
281
|
{
|
|
228
282
|
rel: 'canonical',
|
|
229
283
|
href: absolute(localeLink(preferredPath.value) ?? preferredPath.value)
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
// Static HTML retains these relations when an edge serves a prerendered
|
|
287
|
+
// page, unlike an HTTP Link header configured only at runtime.
|
|
288
|
+
rel: 'alternate',
|
|
289
|
+
type: 'text/markdown',
|
|
290
|
+
href: `${localeLink(path.value) ?? path.value}.md`
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
// Unhead's narrow relation union does not yet include this standard
|
|
294
|
+
// relation, but it still renders arbitrary link relation values.
|
|
295
|
+
rel: 'describedby' as never,
|
|
296
|
+
href: '/llms.txt'
|
|
230
297
|
}
|
|
231
298
|
]
|
|
232
299
|
}));
|
|
@@ -272,8 +339,16 @@ useSchemaOrg([
|
|
|
272
339
|
<!-- The type's own layout owns the page: no reading width and no header of
|
|
273
340
|
ours — see `owned`. The CONTENTS COLUMN is drawn here rather than left to
|
|
274
341
|
the layout, because only the page holds the body it is read from. -->
|
|
275
|
-
|
|
342
|
+
<!-- BOTH BRANCHES HANG ON `page`. A missing page throws its 404 out of
|
|
343
|
+
`setup`, and Vue still renders the component once on the way to the
|
|
344
|
+
error page — with none of the bindings `setup` never got to return. The
|
|
345
|
+
first nested read then threw `Cannot read properties of undefined
|
|
346
|
+
(reading 'fullWidth')` on every client-side navigation to a missing page.
|
|
347
|
+
Without a page there is nothing of this component to draw. -->
|
|
348
|
+
<div v-if="page && owned" class="flex min-w-0 flex-1 gap-10">
|
|
276
349
|
<div class="min-w-0 flex-1 py-8">
|
|
350
|
+
<DuxtAnnouncements placement="above-content" />
|
|
351
|
+
|
|
277
352
|
<DuxtVersionBanner />
|
|
278
353
|
|
|
279
354
|
<DuxtTranslationBanner v-if="untranslated" :from="found?.from" />
|
|
@@ -288,23 +363,45 @@ useSchemaOrg([
|
|
|
288
363
|
does not — and a header narrower than the parameter tables under it
|
|
289
364
|
draws a rule that stops halfway across the page. -->
|
|
290
365
|
<header v-if="!titled" class="mb-8 border-b pb-8">
|
|
291
|
-
<DuxtBreadcrumb
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
366
|
+
<DuxtBreadcrumb v-if="controls.breadcrumb" :path="path" class="mb-3" />
|
|
367
|
+
|
|
368
|
+
<!-- THE TITLE ROW, AND WHY IT IS ALLOWED TO WRAP. This shape repeats
|
|
369
|
+
three times on this page — here, in the compact row below and in
|
|
370
|
+
the written-page header further down — and at 320px all three used
|
|
371
|
+
to push the document sideways. Nothing in the row could give: the
|
|
372
|
+
copy control is `shrink-0` by choice, the row was `nowrap`, and an
|
|
373
|
+
`<h1>` keeps `min-width: auto`, so a title whose longest word is
|
|
374
|
+
wide at `text-4xl` pins the row at that word's min-content. Three
|
|
375
|
+
of duxt's own pages overflowed, *Introduction* by 56px.
|
|
376
|
+
|
|
377
|
+
Below `sm` the control drops to its own line instead, because at
|
|
378
|
+
that width there is no room for a 36px title and a 145px control
|
|
379
|
+
side by side, and a clipped title is worse than a wrapped control.
|
|
380
|
+
`sm:flex-nowrap` keeps the control beside the title everywhere
|
|
381
|
+
else: `flex-wrap` breaks lines on an item's MAX-content, so
|
|
382
|
+
wrapping unconditionally would demote the control below any
|
|
383
|
+
ordinary long title on a desktop, which is not the bug.
|
|
384
|
+
|
|
385
|
+
`min-w-0` plus `wrap-break-word` on the heading is the backstop for
|
|
386
|
+
the case wrapping cannot reach — a single word wider than the whole
|
|
387
|
+
column. `overflow-wrap: break-word` does not lower an element's
|
|
388
|
+
min-content width, so without `min-w-0` it can never act inside a
|
|
389
|
+
flex row; and because it only breaks a word that fits on no line of
|
|
390
|
+
its own, an ordinary title is left exactly as it was. -->
|
|
391
|
+
<div
|
|
392
|
+
class="flex flex-wrap items-start justify-between gap-x-4 gap-y-2 sm:flex-nowrap"
|
|
393
|
+
>
|
|
298
394
|
<h1
|
|
299
395
|
ref="heading"
|
|
300
396
|
tabindex="-1"
|
|
301
|
-
class="text-4xl font-semibold tracking-tight text-balance outline-none"
|
|
397
|
+
class="min-w-0 text-4xl font-semibold tracking-tight text-balance wrap-break-word outline-none"
|
|
302
398
|
>
|
|
303
399
|
{{ page?.title }}
|
|
304
400
|
</h1>
|
|
305
401
|
|
|
306
402
|
<DuxtCopyPage
|
|
307
|
-
|
|
403
|
+
v-if="controls.copyPage"
|
|
404
|
+
class="mt-1 ms-auto"
|
|
308
405
|
:path="path"
|
|
309
406
|
:title="page?.title"
|
|
310
407
|
:rawbody="(page as { rawbody?: string })?.rawbody"
|
|
@@ -328,11 +425,15 @@ useSchemaOrg([
|
|
|
328
425
|
|
|
329
426
|
<!-- A type that DOES draw its own title keeps the compact row: where the
|
|
330
427
|
reader is, and what they can do with the page. -->
|
|
331
|
-
<div
|
|
332
|
-
|
|
428
|
+
<div
|
|
429
|
+
v-else
|
|
430
|
+
class="mb-6 flex flex-wrap items-start justify-between gap-x-4 gap-y-2 sm:flex-nowrap"
|
|
431
|
+
>
|
|
432
|
+
<DuxtBreadcrumb v-if="controls.breadcrumb" :path="path" />
|
|
333
433
|
|
|
334
434
|
<DuxtCopyPage
|
|
335
|
-
|
|
435
|
+
v-if="controls.copyPage"
|
|
436
|
+
class="-mt-1 ms-auto"
|
|
336
437
|
:path="path"
|
|
337
438
|
:title="page?.title"
|
|
338
439
|
:rawbody="(page as { rawbody?: string })?.rawbody"
|
|
@@ -349,7 +450,7 @@ useSchemaOrg([
|
|
|
349
450
|
at the section's edge, so the links can only be siblings — and a
|
|
350
451
|
release page that ended in whitespace now ends in the release before
|
|
351
452
|
it, which is how a history is read. -->
|
|
352
|
-
<DuxtPageNav :path="path" />
|
|
453
|
+
<DuxtPageNav v-if="controls.prevNext" :path="path" />
|
|
353
454
|
|
|
354
455
|
<!-- Provenance survives the header, because it is the one part of it that
|
|
355
456
|
is still true: every page of a generated section came out of one
|
|
@@ -359,7 +460,7 @@ useSchemaOrg([
|
|
|
359
460
|
<!-- As a row, and as wide as the page: with no column to sit in it is a
|
|
360
461
|
footer, and a rule that stopped at the reading measure under a page
|
|
361
462
|
set wider than that ended halfway across. -->
|
|
362
|
-
<DuxtPageInfo v-if="!aside" row :page="page" />
|
|
463
|
+
<DuxtPageInfo v-if="!aside && controls.pageInfo" row :page="page" />
|
|
363
464
|
</div>
|
|
364
465
|
|
|
365
466
|
<div v-if="aside" class="hidden w-56 shrink-0 xl:block">
|
|
@@ -368,37 +469,46 @@ useSchemaOrg([
|
|
|
368
469
|
>
|
|
369
470
|
<DuxtToc :links="generated" />
|
|
370
471
|
|
|
371
|
-
<DuxtPageInfo :page="page" />
|
|
472
|
+
<DuxtPageInfo v-if="controls.pageInfo" :page="page" />
|
|
372
473
|
</div>
|
|
373
474
|
</div>
|
|
374
475
|
</div>
|
|
375
476
|
|
|
376
|
-
<div v-else class="flex min-w-0 flex-1 justify-center gap-10">
|
|
377
|
-
|
|
477
|
+
<div v-else-if="page" class="flex min-w-0 flex-1 justify-center gap-10">
|
|
478
|
+
<!-- `fullWidth` removes the reading measure and NOTHING else: the header,
|
|
479
|
+
the left navigation and the column beside the article all stay, and a
|
|
480
|
+
page that wants those gone says so with their own controls. It is the
|
|
481
|
+
one page control that is off until asked for, because it removes a
|
|
482
|
+
constraint rather than something the shell draws. -->
|
|
483
|
+
<article
|
|
484
|
+
class="min-w-0 flex-1 py-8"
|
|
485
|
+
:class="controls.fullWidth ? undefined : 'max-w-3xl'"
|
|
486
|
+
>
|
|
487
|
+
<DuxtAnnouncements placement="above-content" />
|
|
488
|
+
|
|
378
489
|
<DuxtVersionBanner />
|
|
379
490
|
|
|
380
491
|
<DuxtTranslationBanner v-if="untranslated" :from="found?.from" />
|
|
381
492
|
|
|
382
493
|
<header class="mb-8 border-b pb-8">
|
|
383
|
-
<DuxtBreadcrumb
|
|
384
|
-
v-if="duxt?.breadcrumb !== false"
|
|
385
|
-
:path="path"
|
|
386
|
-
class="mb-3"
|
|
387
|
-
/>
|
|
494
|
+
<DuxtBreadcrumb v-if="controls.breadcrumb" :path="path" class="mb-3" />
|
|
388
495
|
<!-- The copy action sits with the title, not under the article: it is
|
|
389
496
|
what a reader does with the page BEFORE reading it, and a control
|
|
390
497
|
for that at the bottom is a control nobody finds. -->
|
|
391
|
-
<div
|
|
498
|
+
<div
|
|
499
|
+
class="flex flex-wrap items-start justify-between gap-x-4 gap-y-2 sm:flex-nowrap"
|
|
500
|
+
>
|
|
392
501
|
<h1
|
|
393
502
|
ref="heading"
|
|
394
503
|
tabindex="-1"
|
|
395
|
-
class="text-4xl font-semibold tracking-tight text-balance outline-none"
|
|
504
|
+
class="min-w-0 text-4xl font-semibold tracking-tight text-balance wrap-break-word outline-none"
|
|
396
505
|
>
|
|
397
506
|
{{ page?.title }}
|
|
398
507
|
</h1>
|
|
399
508
|
|
|
400
509
|
<DuxtCopyPage
|
|
401
|
-
|
|
510
|
+
v-if="controls.copyPage"
|
|
511
|
+
class="mt-1 ms-auto"
|
|
402
512
|
:path="path"
|
|
403
513
|
:title="page?.title"
|
|
404
514
|
:rawbody="(page as { rawbody?: string })?.rawbody"
|
|
@@ -416,25 +526,26 @@ useSchemaOrg([
|
|
|
416
526
|
<ContentRenderer v-if="page" :value="page" />
|
|
417
527
|
</div>
|
|
418
528
|
|
|
419
|
-
<DuxtPageNav :path="path" />
|
|
529
|
+
<DuxtPageNav v-if="controls.prevNext" :path="path" />
|
|
420
530
|
|
|
421
|
-
<DuxtPageFeedback />
|
|
531
|
+
<DuxtPageFeedback v-if="controls.feedback" />
|
|
422
532
|
</article>
|
|
423
533
|
|
|
424
534
|
<!-- A div for the same reason as the sidebar's: DuxtToc's two <nav>s are
|
|
425
535
|
the landmarks, and both are labelled. -->
|
|
426
|
-
|
|
536
|
+
<!-- It drops out entirely once a page has emptied it — see `column`. -->
|
|
537
|
+
<div v-if="column" class="hidden w-56 shrink-0 xl:block">
|
|
427
538
|
<div
|
|
428
539
|
class="sticky top-[var(--duxt-header-offset)] max-h-[calc(100vh-var(--duxt-header-offset)-1.5rem)] overflow-y-auto py-8"
|
|
429
540
|
>
|
|
430
|
-
<DuxtToc :links="
|
|
541
|
+
<DuxtToc :links="outline" />
|
|
431
542
|
|
|
432
543
|
<!-- Provenance under the contents: where this page came from, when it
|
|
433
544
|
last changed and who wrote it. On the right rather than under the
|
|
434
545
|
article, where it read as an afterthought below the prev/next
|
|
435
546
|
links — this column is already the one answering what a page IS
|
|
436
547
|
rather than what it says. -->
|
|
437
|
-
<DuxtPageInfo :page="page" />
|
|
548
|
+
<DuxtPageInfo v-if="controls.pageInfo" :page="page" />
|
|
438
549
|
</div>
|
|
439
550
|
</div>
|
|
440
551
|
</div>
|
package/app/pages/index.vue
CHANGED
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
const duxt = useDuxtConfig();
|
|
22
22
|
const localeLink = useDuxtLink();
|
|
23
23
|
const { t } = useI18n();
|
|
24
|
-
const notify = useDuxtToast();
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
26
|
* Where a hero button goes when the config did not say — the same fallback
|
|
@@ -48,22 +47,11 @@ const { data: highlightedCommand } = await useAsyncData(
|
|
|
48
47
|
{ watch: [command] }
|
|
49
48
|
);
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
await navigator.clipboard.writeText(command.value);
|
|
57
|
-
copied.value = true;
|
|
58
|
-
// The icon alone is a 3.5 rem change under the reader's own cursor, which
|
|
59
|
-
// is exactly where they are not looking after a click. The toast is the
|
|
60
|
-
// confirmation; the icon is what stays for the two seconds after it.
|
|
61
|
-
notify.success(t('duxt.code.copiedToast'));
|
|
62
|
-
setTimeout(() => (copied.value = false), 2000);
|
|
63
|
-
} catch {
|
|
64
|
-
notify.error(t('duxt.page.copyFailed'));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
50
|
+
// The icon alone is a 3.5 rem change under the reader's own cursor, which is
|
|
51
|
+
// exactly where they are not looking after a click. The toast is the
|
|
52
|
+
// confirmation; the icon is what stays for the two seconds after it.
|
|
53
|
+
const { copied, copy } = useDuxtCopy();
|
|
54
|
+
const copyCommand = () => copy(command.value);
|
|
67
55
|
|
|
68
56
|
/**
|
|
69
57
|
* The badge, in either of its two shapes. A string is the label and nothing
|
|
@@ -275,12 +263,16 @@ defineOgImage('Duxt', {
|
|
|
275
263
|
<span aria-hidden="true" class="text-muted-foreground select-none"
|
|
276
264
|
>$</span
|
|
277
265
|
>
|
|
266
|
+
<!-- A shell command does not turn round with the interface, so
|
|
267
|
+
it declares its own direction and aligns to the start of THAT
|
|
268
|
+
— which is the left, in either locale. -->
|
|
278
269
|
<span
|
|
279
270
|
v-if="highlightedCommand"
|
|
280
|
-
|
|
271
|
+
dir="ltr"
|
|
272
|
+
class="min-w-0 overflow-x-auto text-start [&_pre]:bg-transparent!"
|
|
281
273
|
v-html="highlightedCommand"
|
|
282
274
|
/>
|
|
283
|
-
<span v-else class="min-w-0 overflow-x-auto text-
|
|
275
|
+
<span v-else dir="ltr" class="min-w-0 overflow-x-auto text-start">{{
|
|
284
276
|
command
|
|
285
277
|
}}</span>
|
|
286
278
|
<Icon
|
|
@@ -412,7 +404,7 @@ defineOgImage('Duxt', {
|
|
|
412
404
|
? 'lucide:arrow-up-right'
|
|
413
405
|
: 'lucide:arrow-right'
|
|
414
406
|
"
|
|
415
|
-
class="size-3.5 text-muted-foreground transition-transform group-hover:translate-x-0.5"
|
|
407
|
+
class="size-3.5 text-muted-foreground rtl:-scale-x-100 transition-transform ltr:group-hover:translate-x-0.5 rtl:group-hover:-translate-x-0.5"
|
|
416
408
|
/>
|
|
417
409
|
</UiCardTitle>
|
|
418
410
|
|
|
@@ -425,9 +417,9 @@ defineOgImage('Duxt', {
|
|
|
425
417
|
</div>
|
|
426
418
|
</section>
|
|
427
419
|
|
|
428
|
-
<!-- THE
|
|
429
|
-
|
|
430
|
-
|
|
420
|
+
<!-- THE SUPPORTING SYSTEM. One line each, no cards and no links: these are
|
|
421
|
+
real parts of publishing and reading the content, while a card apiece
|
|
422
|
+
would say they matter as much as the primary capabilities above.
|
|
431
423
|
|
|
432
424
|
A <ul>, and the entries are paragraphs rather than headings: this is a
|
|
433
425
|
list of things, not a part of the document with sections under it — and
|