blume 1.5.0 → 1.5.1
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/CHANGELOG.md +15 -0
- package/README.md +16 -12
- package/dist/cli/index.js +130 -24
- package/dist/cli/index.js.map +10 -10
- package/dist/types/core/config-input.d.ts +1 -1
- package/dist/types/core/data.d.ts +3 -2
- package/dist/types/theme/fonts.d.ts +11 -2
- package/docs/advanced/custom-pages.mdx +5 -1
- package/docs/configuration/index.mdx +1 -1
- package/docs/configuration/theming.mdx +4 -2
- package/docs/reference/cli.mdx +1 -1
- package/package.json +1 -1
- package/src/astro/generate.ts +10 -5
- package/src/astro/templates.ts +30 -8
- package/src/components/layout/Fonts.astro +23 -3
- package/src/components/layout/PageLayout.astro +72 -3
- package/src/components/layout/ReferenceLayout.astro +2 -1
- package/src/components/layout/RootLayout.astro +2 -1
- package/src/core/config-input.ts +1 -1
- package/src/core/data.ts +3 -2
- package/src/core/last-modified.ts +49 -0
- package/src/core/project-graph.ts +11 -0
- package/src/core/schema.ts +1 -1
- package/src/deploy/vercel-negotiation.ts +34 -14
- package/src/og/card.ts +3 -1
- package/src/theme/entry.ts +24 -2
- package/src/theme/fonts.ts +75 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# blume
|
|
2
2
|
|
|
3
|
+
## 1.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ca28fb8: Generate an Open Graph card for the changelog index. The `/changelog` page previously shipped no `og:image` (and fell back to X's compact summary card); it now gets the same generated card every other page has, served at `/og/changelog.png`.
|
|
8
|
+
- ca28fb8: Stop doubling the site title in the changelog index's document title. The page passed "{site title} {Changelog}" to a layout that suffixes "- {site title}" itself, producing titles like "Acme Changelog - Acme"; it now reads "Changelog - Acme".
|
|
9
|
+
- 8db1ecb: The default display font is now Inter, matching the body font — default sites download one text family instead of two (Inter Tight's files alone were ~190 KB per page). To keep the tightened display look, headings now get `letter-spacing: -0.05em` from the theme itself, which also means any font you configure for `display` reads correctly at heading sizes instead of depending on tracking built into the font. Inter Tight remains available as the `inter-tight` slug.
|
|
10
|
+
- ca28fb8: Let a user-supplied `ogImage` on `PageLayout` declare its alt text and pixel size via the new `ogImageAlt` and `ogImageSize` props, emitted as `og:image:alt` / `og:image:width` / `og:image:height` — the metadata Blume's generated cards already declare automatically.
|
|
11
|
+
- ca28fb8: Emit schema.org JSON-LD from `PageLayout`, so custom pages — most importantly a custom home page — carry the same `WebSite` structured-data graph the docs pages do. On by default; pass `structuredDataEnabled={false}` to opt a page out.
|
|
12
|
+
- ca28fb8: Keep the trailing slash on the home page's canonical, `og:url`, and hreflang URLs (`https://site/` instead of `https://site`), so they byte-match the sitemap's `<loc>` for the root route.
|
|
13
|
+
- ca28fb8: Warn when `lastModified` runs in a shallow git clone. CI platforms usually check out limited history, which silently dropped most git-derived dates — sitemap `<lastmod>` and "Last updated" stamps vanished in production while working locally. The build now emits a `BLUME_SHALLOW_GIT_HISTORY` warning pointing at the fix (`VERCEL_DEEP_CLONE=true` on Vercel, `fetch-depth: 0` for actions/checkout).
|
|
14
|
+
- bad025f: Smooth out page navigation: every link now prefetches on hover/viewport, and same-origin navigations opt into cross-document view transitions in supporting browsers, replacing the hard flash between pages with a crossfade. Respects `prefers-reduced-motion`.
|
|
15
|
+
- 8db1ecb: Preload only the font weights above-the-fold text actually renders in (body 400/500, display 500/600, mono 400) instead of every configured face. On a default site this cuts the per-page font preloads from ten files (~260 KB) to a handful (~50 KB), bandwidth that was competing with the critical CSS and pushing out mobile LCP. All other faces still load on demand through their `@font-face` rules with `font-display: swap`.
|
|
16
|
+
- ca28fb8: Redirect trailing-slash URLs to their slashless twins on Vercel with a 308. `/docs/` and `/docs` previously both served 200 as duplicate URLs; the routing config now collapses the slashed form onto the canonical slashless one (the root `/` is untouched).
|
|
17
|
+
|
|
3
18
|
## 1.5.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -49,18 +49,22 @@ Blume works with any package manager and never requires you to set up Astro or T
|
|
|
49
49
|
|
|
50
50
|
## CLI
|
|
51
51
|
|
|
52
|
-
| Command
|
|
53
|
-
|
|
|
54
|
-
| `blume init [dir]` | Scaffold a project (interactive by default).
|
|
55
|
-
| `blume dev`
|
|
56
|
-
| `blume build`
|
|
57
|
-
| `blume preview`
|
|
58
|
-
| `blume add <item>` | Install a source component from the registry.
|
|
59
|
-
| `blume sync`
|
|
60
|
-
| `blume eject`
|
|
61
|
-
| `blume check`
|
|
62
|
-
| `blume validate`
|
|
63
|
-
| `blume doctor`
|
|
52
|
+
| Command | Description |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `blume init [dir]` | Scaffold a project (interactive by default). |
|
|
55
|
+
| `blume dev` | Start the dev server with hot reload. |
|
|
56
|
+
| `blume build` | Build the static (or server) site. |
|
|
57
|
+
| `blume preview` | Preview the last build. |
|
|
58
|
+
| `blume add <item>` | Install a source component from the registry. |
|
|
59
|
+
| `blume sync` | Re-fetch remote content sources and regenerate. |
|
|
60
|
+
| `blume eject` | Promote the runtime into a standalone Astro app. |
|
|
61
|
+
| `blume check` | Type-check the docs site with `astro check`. |
|
|
62
|
+
| `blume validate` | Validate internal, anchor, asset, and external links. |
|
|
63
|
+
| `blume doctor` | Diagnose config and content problems. |
|
|
64
|
+
| `blume audit` | Audit the built site for SEO and health issues. |
|
|
65
|
+
| `blume eval` | Test the docs: an agent answers your questions using only the documentation. |
|
|
66
|
+
| `blume translate` | Translate docs into the configured locales with a local agent CLI. |
|
|
67
|
+
| `blume version [id]` | Freeze the current docs as an archived version (no id lists configured versions). |
|
|
64
68
|
|
|
65
69
|
See the [CLI reference](https://useblume.dev/docs/reference/cli) for every flag.
|
|
66
70
|
|
package/dist/cli/index.js
CHANGED
|
@@ -3607,7 +3607,47 @@ ${lines.join(`
|
|
|
3607
3607
|
}
|
|
3608
3608
|
` : "";
|
|
3609
3609
|
};
|
|
3610
|
-
var
|
|
3610
|
+
var PRELOAD_WEIGHTS = {
|
|
3611
|
+
body: [400, 500],
|
|
3612
|
+
display: [500, 600],
|
|
3613
|
+
mono: [400]
|
|
3614
|
+
};
|
|
3615
|
+
var entryWeights = (entry) => entry.kind === "remote" ? entry.weights : entry.variants.map((variant) => variant.weight);
|
|
3616
|
+
var preloadWeightsFor = (slot, entry) => {
|
|
3617
|
+
const preferred = PRELOAD_WEIGHTS[slot];
|
|
3618
|
+
const weights = entryWeights(entry);
|
|
3619
|
+
const numeric = weights.filter((weight) => typeof weight === "number");
|
|
3620
|
+
const hits = preferred.filter((weight) => numeric.includes(weight));
|
|
3621
|
+
if (hits.length > 0) {
|
|
3622
|
+
return hits;
|
|
3623
|
+
}
|
|
3624
|
+
return numeric.length === weights.length ? numeric : preferred;
|
|
3625
|
+
};
|
|
3626
|
+
var configuredFonts = (fonts) => {
|
|
3627
|
+
if (!fonts) {
|
|
3628
|
+
return [];
|
|
3629
|
+
}
|
|
3630
|
+
const heads = new Map;
|
|
3631
|
+
for (const slot of SLOTS) {
|
|
3632
|
+
const value = fonts[slot];
|
|
3633
|
+
if (value === undefined) {
|
|
3634
|
+
continue;
|
|
3635
|
+
}
|
|
3636
|
+
const entry = resolveFontValue(slot, value);
|
|
3637
|
+
if (!entry) {
|
|
3638
|
+
continue;
|
|
3639
|
+
}
|
|
3640
|
+
const weights = heads.get(entry.cssVariable) ?? new Set;
|
|
3641
|
+
for (const weight of preloadWeightsFor(slot, entry)) {
|
|
3642
|
+
weights.add(weight);
|
|
3643
|
+
}
|
|
3644
|
+
heads.set(entry.cssVariable, weights);
|
|
3645
|
+
}
|
|
3646
|
+
return [...heads].map(([cssVariable, weights]) => ({
|
|
3647
|
+
cssVariable,
|
|
3648
|
+
preloadWeights: [...weights].toSorted((a, b) => a - b)
|
|
3649
|
+
}));
|
|
3650
|
+
};
|
|
3611
3651
|
|
|
3612
3652
|
// src/astro/templates.ts
|
|
3613
3653
|
var WORKSPACE_MARKERS = [
|
|
@@ -3936,6 +3976,11 @@ ${userConfigSetup}export default defineConfig({
|
|
|
3936
3976
|
},
|
|
3937
3977
|
},
|
|
3938
3978
|
devToolbar: { enabled: false },
|
|
3979
|
+
// Navigations are full document loads (no client router), so the next page's
|
|
3980
|
+
// HTML is fetched on hover/viewport to hide the request latency behind the
|
|
3981
|
+
// user's intent. Pairs with the cross-document view-transition rule in the
|
|
3982
|
+
// theme sheet, which smooths the swap itself.
|
|
3983
|
+
prefetch: { prefetchAll: true },
|
|
3939
3984
|
vite: {
|
|
3940
3985
|
plugins: [tailwindcss(), prerenderDepsPlugin(), serverAppResolvePlugin()],
|
|
3941
3986
|
// Everything hydration can reach must be part of the dev dep optimizer's
|
|
@@ -4444,7 +4489,7 @@ export function GET({ props }: { props: { section: string } }) {
|
|
|
4444
4489
|
});
|
|
4445
4490
|
}
|
|
4446
4491
|
`;
|
|
4447
|
-
var ogEndpointTemplate = (customRoutes = [], og = {}) => `// Generated by Blume. Do not edit.
|
|
4492
|
+
var ogEndpointTemplate = (customRoutes = [], og = {}, includeChangelog = false) => `// Generated by Blume. Do not edit.
|
|
4448
4493
|
import { renderOgImage } from "blume/og";
|
|
4449
4494
|
import type { OgFont, OgFontFamilies } from "blume/og";
|
|
4450
4495
|
import data from "blume:data";
|
|
@@ -4479,7 +4524,10 @@ export function getStaticPaths() {
|
|
|
4479
4524
|
}
|
|
4480
4525
|
for (const route of data.routes) {
|
|
4481
4526
|
add(route.path === "/" ? "index" : route.path.slice(1), route.title);
|
|
4482
|
-
}
|
|
4527
|
+
}${includeChangelog ? `
|
|
4528
|
+
// The generated changelog index is not a content route, so it needs its own
|
|
4529
|
+
// card. Added last: a custom page or content route owning /changelog wins.
|
|
4530
|
+
add("changelog", data.ui.changelog?.title ?? "Changelog");` : ""}
|
|
4483
4531
|
return paths;
|
|
4484
4532
|
}
|
|
4485
4533
|
|
|
@@ -4753,10 +4801,9 @@ const contentLocale =
|
|
|
4753
4801
|
const contentDir = i18n
|
|
4754
4802
|
? (i18n.locales.find((l) => l.code === contentLocale)?.dir ?? "ltr")
|
|
4755
4803
|
: "ltr";
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
};
|
|
4804
|
+
// The root route keeps its trailing slash (\`https://site/\`) so canonical and
|
|
4805
|
+
// hreflang URLs byte-match the sitemap's <loc> for the home page.
|
|
4806
|
+
const absolute = (path: string) => base + withBase(path);
|
|
4760
4807
|
|
|
4761
4808
|
// An archived page defaults its canonical to the same page in the latest docs
|
|
4762
4809
|
// when that page still exists — search engines treat the live page as
|
|
@@ -4767,7 +4814,7 @@ const canonical =
|
|
|
4767
4814
|
(archived && archived.canonical === "latest" && latestVersionAlt && base
|
|
4768
4815
|
? absolute(latestVersionAlt.path)
|
|
4769
4816
|
: base
|
|
4770
|
-
? \`\${base}\${basedRoute === "/" ? "" : encodeURI(basedRoute)}\`
|
|
4817
|
+
? \`\${base}\${basedRoute === "/" ? "/" : encodeURI(basedRoute)}\`
|
|
4771
4818
|
: null);
|
|
4772
4819
|
const effectiveNoindex = Boolean(seo.noindex) || (archived?.noindex ?? false);
|
|
4773
4820
|
|
|
@@ -5061,6 +5108,12 @@ const base = data.config.site ? data.config.site.replace(/\\/$/, "") : null;
|
|
|
5061
5108
|
const basedRoute = withBase("/changelog");
|
|
5062
5109
|
const canonical = base ? base + basedRoute : null;
|
|
5063
5110
|
|
|
5111
|
+
// The generated OG card for this route (the /og endpoint emits it alongside
|
|
5112
|
+
// the content-route cards), absolutized like the catch-all's so crawlers get
|
|
5113
|
+
// a full URL when the site is known.
|
|
5114
|
+
const ogPath = data.config.og.enabled ? withBase("/og/changelog.png") : null;
|
|
5115
|
+
const ogImage = ogPath && base ? base + ogPath : ogPath;
|
|
5116
|
+
|
|
5064
5117
|
// The page chrome (h1, title, description) comes from the same translatable
|
|
5065
5118
|
// \`changelog\` group as the reveal button; optional chaining tolerates a
|
|
5066
5119
|
// not-yet-regenerated data snapshot from before these keys existed.
|
|
@@ -5068,7 +5121,10 @@ const changelogTitle = data.ui.changelog?.title ?? "Changelog";
|
|
|
5068
5121
|
const changelogDescription =
|
|
5069
5122
|
data.ui.changelog?.description ??
|
|
5070
5123
|
"Product updates, new features, and fixes from every release.";
|
|
5071
|
-
|
|
5124
|
+
// The layout suffixes "- {site title}" itself, so the page title is just the
|
|
5125
|
+
// changelog's own name — prefixing the site title too would double it
|
|
5126
|
+
// ("Acme Changelog - Acme").
|
|
5127
|
+
const pageTitle = changelogTitle;
|
|
5072
5128
|
|
|
5073
5129
|
const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
5074
5130
|
---
|
|
@@ -5100,7 +5156,8 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
5100
5156
|
fontCssVars={data.fontCssVars}
|
|
5101
5157
|
searchEnabled={data.config.search.enabled}
|
|
5102
5158
|
indexable={true}
|
|
5103
|
-
ogImage={
|
|
5159
|
+
ogImage={ogImage}
|
|
5160
|
+
ogGenerated={Boolean(ogImage)}
|
|
5104
5161
|
x={data.config.x}
|
|
5105
5162
|
canonical={canonical}
|
|
5106
5163
|
exportPdf={${options.exportPdf}}
|
|
@@ -9861,7 +9918,7 @@ var themeConfigSchema = z2.strictObject({
|
|
|
9861
9918
|
backgroundImage: perModeValueSchema,
|
|
9862
9919
|
fonts: z2.strictObject({
|
|
9863
9920
|
body: fontValueSchema.default("inter"),
|
|
9864
|
-
display: fontValueSchema.default("inter
|
|
9921
|
+
display: fontValueSchema.default("inter"),
|
|
9865
9922
|
mono: fontValueSchema.default("ibm-plex-mono")
|
|
9866
9923
|
}).prefault({}),
|
|
9867
9924
|
layout: z2.enum(["sidebar"]).default("sidebar"),
|
|
@@ -11458,6 +11515,26 @@ var gitLastModifiedTimes = (root, contentRoots, sourcePaths) => {
|
|
|
11458
11515
|
return new Map;
|
|
11459
11516
|
}
|
|
11460
11517
|
};
|
|
11518
|
+
var isShallowGitRepository = (root) => {
|
|
11519
|
+
try {
|
|
11520
|
+
return execFileSync("git", ["-C", root, "rev-parse", "--is-shallow-repository"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim() === "true";
|
|
11521
|
+
} catch {
|
|
11522
|
+
return false;
|
|
11523
|
+
}
|
|
11524
|
+
};
|
|
11525
|
+
var lastModifiedShallowWarning = (root, undatedCount) => {
|
|
11526
|
+
if (undatedCount === 0 || !isShallowGitRepository(root)) {
|
|
11527
|
+
return [];
|
|
11528
|
+
}
|
|
11529
|
+
return [
|
|
11530
|
+
{
|
|
11531
|
+
code: "BLUME_SHALLOW_GIT_HISTORY",
|
|
11532
|
+
message: `lastModified is on, but this build runs in a shallow git clone, so ${undatedCount} page(s) have no git-derived date — their sitemap <lastmod> and "Last updated" stamps are omitted.`,
|
|
11533
|
+
severity: "warning",
|
|
11534
|
+
suggestion: "Fetch full history in CI: set the VERCEL_DEEP_CLONE=true environment variable on Vercel, or fetch-depth: 0 for actions/checkout."
|
|
11535
|
+
}
|
|
11536
|
+
];
|
|
11537
|
+
};
|
|
11461
11538
|
|
|
11462
11539
|
// src/core/manifest.ts
|
|
11463
11540
|
var versionAlternateKey = (versionKey, locale) => `${versionKey}\x00${locale}`;
|
|
@@ -14029,6 +14106,7 @@ var scanProject = async (root, options = {}) => {
|
|
|
14029
14106
|
} = normalizeLoadedEntries(loaded, config);
|
|
14030
14107
|
const pages = mode === "build" && !preview ? allPages.filter((page) => !page.meta.draft) : allPages;
|
|
14031
14108
|
const lastModified = resolveLastModifiedConfig(config.lastModified);
|
|
14109
|
+
const lastModifiedWarnings = [];
|
|
14032
14110
|
if (lastModified.enabled && lastModified.source === "git") {
|
|
14033
14111
|
const fsPaths = pages.map((page) => page.sourcePath).filter((path) => path !== undefined);
|
|
14034
14112
|
const contentRoots = sources.flatMap((source) => source.staged || !source.contentRoot ? [] : [source.contentRoot]);
|
|
@@ -14038,6 +14116,8 @@ var scanProject = async (root, options = {}) => {
|
|
|
14038
14116
|
page.lastModified = gitTimes.get(page.sourcePath);
|
|
14039
14117
|
}
|
|
14040
14118
|
}
|
|
14119
|
+
const undated = pages.filter((page) => page.sourcePath && !page.lastModified).length;
|
|
14120
|
+
lastModifiedWarnings.push(...lastModifiedShallowWarning(context.root, undated));
|
|
14041
14121
|
}
|
|
14042
14122
|
const graph = buildContentGraph(pages, {
|
|
14043
14123
|
basePath: config.basePath,
|
|
@@ -14059,7 +14139,8 @@ var scanProject = async (root, options = {}) => {
|
|
|
14059
14139
|
...entryIdDiagnostics(pages, resolveDocsCollection(config, context).base),
|
|
14060
14140
|
...graph.diagnostics,
|
|
14061
14141
|
...i18nWarnings,
|
|
14062
|
-
...versionWarnings
|
|
14142
|
+
...versionWarnings,
|
|
14143
|
+
...lastModifiedWarnings
|
|
14063
14144
|
],
|
|
14064
14145
|
droppedPages,
|
|
14065
14146
|
graph,
|
|
@@ -15818,12 +15899,14 @@ var buildNegotiationRoutes = (routePaths, homeTokens) => {
|
|
|
15818
15899
|
return { headerRoutes, rewriteRoutes };
|
|
15819
15900
|
};
|
|
15820
15901
|
var HOME_SRC = "^/$";
|
|
15821
|
-
var
|
|
15902
|
+
var TRAILING_SLASH_REDIRECT = {
|
|
15903
|
+
headers: { Location: "/$1" },
|
|
15904
|
+
src: "^/(.+)/$",
|
|
15905
|
+
status: 308
|
|
15906
|
+
};
|
|
15907
|
+
var isNegotiationRoute = (route) => route.has?.some((condition) => condition.value === ACCEPT_MARKDOWN_HEADER_VALUE) === true || route.continue === true && route.headers?.vary === "Accept" && isString6(route.src) && Object.keys(route).length === 3 || route.continue === true && isString6(route.headers?.link) && route.src === HOME_SRC && Object.keys(route).length === 3 || route.status === TRAILING_SLASH_REDIRECT.status && route.src === TRAILING_SLASH_REDIRECT.src;
|
|
15822
15908
|
var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTypeOverrides, homeTokens) => {
|
|
15823
15909
|
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
15824
|
-
if (routePaths.length === 0 && !homeLinkHeader && overrideEntries.length === 0) {
|
|
15825
|
-
return null;
|
|
15826
|
-
}
|
|
15827
15910
|
let config;
|
|
15828
15911
|
try {
|
|
15829
15912
|
config = JSON.parse(configText);
|
|
@@ -15849,7 +15932,7 @@ var injectNegotiationRoutes = (configText, routePaths, homeLinkHeader, contentTy
|
|
|
15849
15932
|
src: HOME_SRC
|
|
15850
15933
|
});
|
|
15851
15934
|
}
|
|
15852
|
-
routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes);
|
|
15935
|
+
routes.splice(filesystemIndex, 0, ...headerRoutes, ...rewriteRoutes, TRAILING_SLASH_REDIRECT);
|
|
15853
15936
|
config.routes = routes;
|
|
15854
15937
|
return `${JSON.stringify(config, null, "\t")}
|
|
15855
15938
|
`;
|
|
@@ -17319,7 +17402,11 @@ ${THEME_MAPPING}
|
|
|
17319
17402
|
scroll-padding-top: 4.5rem;
|
|
17320
17403
|
text-rendering: optimizeLegibility;
|
|
17321
17404
|
}
|
|
17322
|
-
/* Headings use the display font (defaults to the body font when unset).
|
|
17405
|
+
/* Headings use the display font (defaults to the body font when unset).
|
|
17406
|
+
The tightened tracking is part of the theme, not the font: display-tuned
|
|
17407
|
+
families bake it into their metrics, but a text family promoted to
|
|
17408
|
+
headings (including the Inter default) reads loose without it. -0.05em
|
|
17409
|
+
was matched visually against Inter Tight, the previous display default. */
|
|
17323
17410
|
h1,
|
|
17324
17411
|
h2,
|
|
17325
17412
|
h3,
|
|
@@ -17327,6 +17414,7 @@ ${THEME_MAPPING}
|
|
|
17327
17414
|
h5,
|
|
17328
17415
|
h6 {
|
|
17329
17416
|
font-family: var(--font-display);
|
|
17417
|
+
letter-spacing: -0.05em;
|
|
17330
17418
|
}
|
|
17331
17419
|
:focus-visible {
|
|
17332
17420
|
outline: 2px solid var(--blume-accent);
|
|
@@ -17347,6 +17435,22 @@ ${THEME_MAPPING}
|
|
|
17347
17435
|
}
|
|
17348
17436
|
}
|
|
17349
17437
|
|
|
17438
|
+
/* Same-origin navigations are full document loads (no client router); opting
|
|
17439
|
+
into cross-document view transitions has the browser crossfade between the
|
|
17440
|
+
old and new page instead of hard-swapping, in browsers that support it.
|
|
17441
|
+
Pairs with the prefetch option in the generated Astro config. */
|
|
17442
|
+
@view-transition {
|
|
17443
|
+
navigation: auto;
|
|
17444
|
+
}
|
|
17445
|
+
|
|
17446
|
+
@media (prefers-reduced-motion: reduce) {
|
|
17447
|
+
::view-transition-group(*),
|
|
17448
|
+
::view-transition-old(*),
|
|
17449
|
+
::view-transition-new(*) {
|
|
17450
|
+
animation: none !important;
|
|
17451
|
+
}
|
|
17452
|
+
}
|
|
17453
|
+
|
|
17350
17454
|
/* Code reads left-to-right regardless of page direction; only the surrounding
|
|
17351
17455
|
chrome mirrors for RTL. Inline code is isolated so LTR identifiers don't
|
|
17352
17456
|
disturb the bidi flow of right-to-left prose. */
|
|
@@ -17382,9 +17486,10 @@ ${THEME_MAPPING}
|
|
|
17382
17486
|
line-height: 1.7;
|
|
17383
17487
|
}
|
|
17384
17488
|
|
|
17489
|
+
/* No letter-spacing here: prose headings inherit the base h1-h6 rule's
|
|
17490
|
+
display tracking, same as headings outside the prose column. */
|
|
17385
17491
|
.prose :where(h1, h2, h3, h4) {
|
|
17386
17492
|
font-weight: 500;
|
|
17387
|
-
letter-spacing: 0;
|
|
17388
17493
|
}
|
|
17389
17494
|
|
|
17390
17495
|
/* A heading can carry one long unbreakable token — an OpenAPI operation's title
|
|
@@ -18793,7 +18898,7 @@ var buildRuntimeData = (project) => {
|
|
|
18793
18898
|
href: feed.path,
|
|
18794
18899
|
title: feed.title
|
|
18795
18900
|
})),
|
|
18796
|
-
fontCssVars:
|
|
18901
|
+
fontCssVars: configuredFonts(config.theme.fonts),
|
|
18797
18902
|
navigation: withRepoUrl(graph.navigation),
|
|
18798
18903
|
navigationByLocale,
|
|
18799
18904
|
navigationByVersion: Object.fromEntries(Object.entries(graph.navigationByVersion).map(([id2, byLocale]) => [
|
|
@@ -18987,6 +19092,7 @@ var generateRuntime = async (project) => {
|
|
|
18987
19092
|
const needsSvelte = frameworks.has("svelte");
|
|
18988
19093
|
const reactCompilerPath = resolveReactCompiler(config, needsReact);
|
|
18989
19094
|
const ogRoutes = customOgRoutes(pages, config.title, config.seo.og.titles);
|
|
19095
|
+
const changelogIndex = hasGeneratedChangelog(project, pages);
|
|
18990
19096
|
const mcp = planMcp(project, srcDir, pages);
|
|
18991
19097
|
pages.push(...mcp.discoveryPages);
|
|
18992
19098
|
const hasStaged = staged.size > 0;
|
|
@@ -19056,9 +19162,9 @@ var generateRuntime = async (project) => {
|
|
|
19056
19162
|
writeMcpFiles(project, mcp, write)
|
|
19057
19163
|
]);
|
|
19058
19164
|
if (config.seo.og.enabled) {
|
|
19059
|
-
await write(join28(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes, projectOgFonts(project)));
|
|
19165
|
+
await write(join28(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes, projectOgFonts(project), changelogIndex));
|
|
19060
19166
|
}
|
|
19061
|
-
if (
|
|
19167
|
+
if (changelogIndex) {
|
|
19062
19168
|
await write(join28(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
|
|
19063
19169
|
exportEpub,
|
|
19064
19170
|
exportPdf,
|
|
@@ -19120,7 +19226,7 @@ var generateRuntime = async (project) => {
|
|
|
19120
19226
|
...pages.map((page) => page.pattern),
|
|
19121
19227
|
...referenceRoutes(config)
|
|
19122
19228
|
]);
|
|
19123
|
-
if (
|
|
19229
|
+
if (changelogIndex) {
|
|
19124
19230
|
navTargetRoutes.add("/changelog");
|
|
19125
19231
|
}
|
|
19126
19232
|
warnings.push(...[
|
|
@@ -23753,5 +23859,5 @@ process.on("unhandledRejection", (error) => {
|
|
|
23753
23859
|
});
|
|
23754
23860
|
runMain(main);
|
|
23755
23861
|
|
|
23756
|
-
//# debugId=
|
|
23862
|
+
//# debugId=A01DCE54DD00393164756E2164756E21
|
|
23757
23863
|
//# sourceMappingURL=index.js.map
|