blume 1.7.2 → 1.7.3
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 +10 -0
- package/dist/cli/chunk-0qymqwzz.js +164 -0
- package/dist/cli/chunk-0qymqwzz.js.map +15 -0
- package/dist/cli/{chunk-9bkjd11x.js → chunk-0xjyb285.js} +1 -1
- package/dist/cli/{chunk-hdm2dkd2.js → chunk-1jefwnfs.js} +13 -7
- package/dist/cli/{chunk-hdm2dkd2.js.map → chunk-1jefwnfs.js.map} +3 -3
- package/dist/cli/{chunk-n9sra6sy.js → chunk-3r45185y.js} +5 -7
- package/dist/cli/{chunk-n9sra6sy.js.map → chunk-3r45185y.js.map} +2 -2
- package/dist/cli/{chunk-cvky9gb2.js → chunk-4x36ddpw.js} +3 -3
- package/dist/cli/{chunk-mqb2ka8m.js → chunk-5093q3n7.js} +12 -12
- package/dist/cli/{chunk-9he6crym.js → chunk-5g0w1e2c.js} +4 -4
- package/dist/cli/{chunk-mt76t7dj.js → chunk-5qk08vmp.js} +11 -11
- package/dist/cli/{chunk-61j18dwk.js → chunk-7s8hm3b6.js} +7 -2
- package/dist/cli/{chunk-61j18dwk.js.map → chunk-7s8hm3b6.js.map} +3 -3
- package/dist/cli/{chunk-196vjxp9.js → chunk-8cjtbafj.js} +11 -9
- package/dist/cli/{chunk-196vjxp9.js.map → chunk-8cjtbafj.js.map} +2 -2
- package/dist/cli/{chunk-aztttvb3.js → chunk-97r59kpr.js} +4 -4
- package/dist/cli/{chunk-t3tj0dgr.js → chunk-ahnw3kxw.js} +8 -8
- package/dist/cli/{chunk-hs3gbh8p.js → chunk-b27xqwn9.js} +3 -3
- package/dist/cli/{chunk-450a7rcr.js → chunk-bf6bt1xt.js} +2 -2
- package/dist/cli/{chunk-eevwt1sc.js → chunk-bvwwhd84.js} +13 -13
- package/dist/cli/{chunk-12dxjqk7.js → chunk-cjtn640a.js} +17 -17
- package/dist/cli/{chunk-12dxjqk7.js.map → chunk-cjtn640a.js.map} +1 -1
- package/dist/cli/{chunk-ra1v2nc2.js → chunk-ct47dqpx.js} +14 -3
- package/dist/cli/{chunk-ra1v2nc2.js.map → chunk-ct47dqpx.js.map} +4 -3
- package/dist/cli/{chunk-ppfvdcd4.js → chunk-dwgcp5sm.js} +1 -1
- package/dist/cli/{chunk-3w7b2vcx.js → chunk-e7f42gdj.js} +2 -2
- package/dist/cli/{chunk-vkrsvbr5.js → chunk-esphfr8p.js} +8 -8
- package/dist/cli/{chunk-vkrsvbr5.js.map → chunk-esphfr8p.js.map} +1 -1
- package/dist/cli/{chunk-fmceyezb.js → chunk-ex56aa81.js} +27 -18
- package/dist/cli/chunk-ex56aa81.js.map +13 -0
- package/dist/cli/{chunk-jbj4qhfw.js → chunk-garjf5z9.js} +2 -2
- package/dist/cli/{chunk-wjt80jps.js → chunk-js7saxwm.js} +14 -18
- package/dist/cli/{chunk-wjt80jps.js.map → chunk-js7saxwm.js.map} +4 -6
- package/dist/cli/{chunk-5n7t497w.js → chunk-k79xp7av.js} +53 -91
- package/dist/cli/chunk-k79xp7av.js.map +39 -0
- package/dist/cli/{chunk-688e0dde.js → chunk-nn13znc2.js} +1 -1
- package/dist/cli/{chunk-ejjx8znq.js → chunk-ps4m1xh4.js} +15 -9
- package/dist/cli/{chunk-ejjx8znq.js.map → chunk-ps4m1xh4.js.map} +3 -3
- package/dist/cli/{chunk-xhtpx3ff.js → chunk-rqy0s5wh.js} +13 -13
- package/dist/cli/{chunk-30e87n55.js → chunk-rz9jmfhz.js} +4 -4
- package/dist/cli/{chunk-exeeb35e.js → chunk-vacwm2hv.js} +2 -2
- package/dist/cli/{chunk-8cd8tj54.js → chunk-yg63d42r.js} +7 -7
- package/dist/cli/index.js +15 -15
- package/dist/types/core/config-input.d.ts +33 -0
- package/dist/types/core/data.d.ts +2 -0
- package/dist/types/core/schema.d.ts +20 -0
- package/dist/types/core/types.d.ts +5 -0
- package/docs/configuration/ask-ai.mdx +14 -0
- package/docs/configuration/index.mdx +3 -1
- package/docs/content/navigation.mdx +3 -0
- package/docs/content/syntax.mdx +10 -0
- package/docs/discoverability/agent-discovery.mdx +82 -1
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/reference/frontmatter.mdx +2 -0
- package/package.json +1 -1
- package/src/ai/agent-readability.ts +5 -0
- package/src/ai/ai-catalog.ts +241 -0
- package/src/ai/link-headers.ts +12 -0
- package/src/ai/llms.ts +6 -0
- package/src/ai/mcp/discovery.ts +1 -1
- package/src/astro/generate.ts +2 -0
- package/src/cli/commands/build.ts +3 -1
- package/src/components/islands/hooks.ts +50 -1
- package/src/components/layout/RootLayout.astro +24 -1
- package/src/components/layout/analytics-client.ts +36 -7
- package/src/core/config-input.ts +34 -0
- package/src/core/data.ts +2 -0
- package/src/core/navigation.ts +22 -3
- package/src/core/schema.ts +32 -0
- package/src/core/types.ts +5 -0
- package/src/deploy/artifacts.ts +12 -1
- package/src/deploy/headers.ts +6 -0
- package/src/deploy/vercel-negotiation.ts +25 -2
- package/src/search/build.ts +25 -3
- package/src/theme/entry.ts +23 -0
- package/dist/cli/chunk-5n7t497w.js.map +0 -40
- package/dist/cli/chunk-88by27n5.js +0 -17
- package/dist/cli/chunk-88by27n5.js.map +0 -10
- package/dist/cli/chunk-fmceyezb.js.map +0 -13
- package/dist/cli/chunk-tqa1s0k8.js +0 -69
- package/dist/cli/chunk-tqa1s0k8.js.map +0 -11
- /package/dist/cli/{chunk-9bkjd11x.js.map → chunk-0xjyb285.js.map} +0 -0
- /package/dist/cli/{chunk-cvky9gb2.js.map → chunk-4x36ddpw.js.map} +0 -0
- /package/dist/cli/{chunk-mqb2ka8m.js.map → chunk-5093q3n7.js.map} +0 -0
- /package/dist/cli/{chunk-9he6crym.js.map → chunk-5g0w1e2c.js.map} +0 -0
- /package/dist/cli/{chunk-mt76t7dj.js.map → chunk-5qk08vmp.js.map} +0 -0
- /package/dist/cli/{chunk-aztttvb3.js.map → chunk-97r59kpr.js.map} +0 -0
- /package/dist/cli/{chunk-t3tj0dgr.js.map → chunk-ahnw3kxw.js.map} +0 -0
- /package/dist/cli/{chunk-hs3gbh8p.js.map → chunk-b27xqwn9.js.map} +0 -0
- /package/dist/cli/{chunk-450a7rcr.js.map → chunk-bf6bt1xt.js.map} +0 -0
- /package/dist/cli/{chunk-eevwt1sc.js.map → chunk-bvwwhd84.js.map} +0 -0
- /package/dist/cli/{chunk-ppfvdcd4.js.map → chunk-dwgcp5sm.js.map} +0 -0
- /package/dist/cli/{chunk-3w7b2vcx.js.map → chunk-e7f42gdj.js.map} +0 -0
- /package/dist/cli/{chunk-jbj4qhfw.js.map → chunk-garjf5z9.js.map} +0 -0
- /package/dist/cli/{chunk-688e0dde.js.map → chunk-nn13znc2.js.map} +0 -0
- /package/dist/cli/{chunk-xhtpx3ff.js.map → chunk-rqy0s5wh.js.map} +0 -0
- /package/dist/cli/{chunk-30e87n55.js.map → chunk-rz9jmfhz.js.map} +0 -0
- /package/dist/cli/{chunk-exeeb35e.js.map → chunk-vacwm2hv.js.map} +0 -0
- /package/dist/cli/{chunk-8cd8tj54.js.map → chunk-yg63d42r.js.map} +0 -0
package/src/core/schema.ts
CHANGED
|
@@ -869,6 +869,23 @@ const llmsTxtObjectSchema = z.strictObject({
|
|
|
869
869
|
|
|
870
870
|
type LlmsTxtResolved = z.output<typeof llmsTxtObjectSchema>;
|
|
871
871
|
|
|
872
|
+
/** The object form of `ai.catalog`; a bare boolean normalizes onto it. */
|
|
873
|
+
const aiCatalogObjectSchema = z.strictObject({
|
|
874
|
+
enabled: z.boolean().default(true),
|
|
875
|
+
/**
|
|
876
|
+
* Representative queries per catalog entry, keyed by the entry's
|
|
877
|
+
* `<namespace>:<name>` (the identifier minus its `urn:air:<host>:` prefix,
|
|
878
|
+
* e.g. `mcp:docs`, `skill:blume`, `api:docs`). Replaces the generated
|
|
879
|
+
* defaults for that entry; 2–5 short natural-language questions the
|
|
880
|
+
* resource can answer, which registries embed for semantic search.
|
|
881
|
+
*/
|
|
882
|
+
queries: z
|
|
883
|
+
.record(z.string().min(1), z.array(z.string().trim().min(1)).min(1))
|
|
884
|
+
.default({}),
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
type AiCatalogResolved = z.output<typeof aiCatalogObjectSchema>;
|
|
888
|
+
|
|
872
889
|
const aiConfigSchema = z.strictObject({
|
|
873
890
|
/**
|
|
874
891
|
* The JSON docs API: the page index, per-page JSON, and navigation under
|
|
@@ -983,6 +1000,21 @@ const aiConfigSchema = z.strictObject({
|
|
|
983
1000
|
}
|
|
984
1001
|
})
|
|
985
1002
|
.optional(),
|
|
1003
|
+
/**
|
|
1004
|
+
* The AI Catalog / ARD manifest at `/.well-known/ai-catalog.json` (mirrored
|
|
1005
|
+
* at `/.well-known/ard.json`): one entry per agent-facing resource the site
|
|
1006
|
+
* publishes — the MCP server card, each agent skill, the JSON docs API's
|
|
1007
|
+
* OpenAPI document, each rendered API reference, and llms.txt — so agent
|
|
1008
|
+
* registries can index the site from its domain alone. Needs a
|
|
1009
|
+
* `deployment.site` (identifiers are domain-anchored URNs). On by default;
|
|
1010
|
+
* the object form overrides the generated representative queries.
|
|
1011
|
+
*/
|
|
1012
|
+
catalog: z
|
|
1013
|
+
.union([z.boolean(), aiCatalogObjectSchema])
|
|
1014
|
+
.default(true)
|
|
1015
|
+
.transform((value): AiCatalogResolved =>
|
|
1016
|
+
isBoolean(value) ? { enabled: value, queries: {} } : value
|
|
1017
|
+
),
|
|
986
1018
|
/**
|
|
987
1019
|
* `llms.txt`/`llms-full.txt` emission. A bare boolean toggles it; the object
|
|
988
1020
|
* form adds `openapi: false` to keep generated API reference pages out of
|
package/src/core/types.ts
CHANGED
|
@@ -220,6 +220,11 @@ export type NavNode =
|
|
|
220
220
|
*/
|
|
221
221
|
display: SidebarDisplay;
|
|
222
222
|
icon?: string;
|
|
223
|
+
/**
|
|
224
|
+
* The group row's link: an explicit-config group's `root`, or the
|
|
225
|
+
* generated folder's index page route. Absent when there is no page at
|
|
226
|
+
* the group's own path, so the row never links to a 404.
|
|
227
|
+
*/
|
|
223
228
|
route?: string;
|
|
224
229
|
/**
|
|
225
230
|
* The group's URL path (its folder route prefix), even when the folder
|
package/src/deploy/artifacts.ts
CHANGED
|
@@ -4,6 +4,11 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { dirname, join, resolve } from "pathe";
|
|
5
5
|
|
|
6
6
|
import { buildAgentReadability } from "../ai/agent-readability.ts";
|
|
7
|
+
import {
|
|
8
|
+
AI_CATALOG_PATH,
|
|
9
|
+
ARD_MANIFEST_PATH,
|
|
10
|
+
buildAiCatalog,
|
|
11
|
+
} from "../ai/ai-catalog.ts";
|
|
7
12
|
import { API_CATALOG_PATH, buildApiCatalog } from "../ai/api-catalog.ts";
|
|
8
13
|
import { buildHomeLinkHeader } from "../ai/link-headers.ts";
|
|
9
14
|
import { buildLlmsFiles } from "../ai/llms.ts";
|
|
@@ -234,8 +239,12 @@ const emitAgentSkills = async (
|
|
|
234
239
|
const emitWellKnownFiles = async (
|
|
235
240
|
config: ResolvedConfig,
|
|
236
241
|
distDir: string,
|
|
242
|
+
skills: readonly SkillArtifact[],
|
|
237
243
|
logger: ArtifactLogger
|
|
238
244
|
): Promise<void> => {
|
|
245
|
+
// One document, two paths: the ai-catalog spec's well-known URI and the
|
|
246
|
+
// ARD v0.91 one (see `ai/ai-catalog.ts`).
|
|
247
|
+
const aiCatalog = buildAiCatalog(config, skills);
|
|
239
248
|
const files = [
|
|
240
249
|
{
|
|
241
250
|
content: buildSignaturesDirectory(config),
|
|
@@ -247,6 +256,8 @@ const emitWellKnownFiles = async (
|
|
|
247
256
|
label: "RFC 9727",
|
|
248
257
|
path: API_CATALOG_PATH,
|
|
249
258
|
},
|
|
259
|
+
{ content: aiCatalog, label: "AI Catalog", path: AI_CATALOG_PATH },
|
|
260
|
+
{ content: aiCatalog, label: "ARD manifest", path: ARD_MANIFEST_PATH },
|
|
250
261
|
];
|
|
251
262
|
for (const file of files) {
|
|
252
263
|
const target = join(distDir, file.path.slice(1));
|
|
@@ -362,7 +373,7 @@ export const publishBuildArtifacts = async (
|
|
|
362
373
|
logger.info("Generated agent-readability.json");
|
|
363
374
|
}
|
|
364
375
|
|
|
365
|
-
await emitWellKnownFiles(project.config, distDir, logger);
|
|
376
|
+
await emitWellKnownFiles(project.config, distDir, skills, logger);
|
|
366
377
|
await emitAgentSkills(project, distDir, skills, logger);
|
|
367
378
|
|
|
368
379
|
await emitRedirectFiles(project.config, distDir, logger);
|
package/src/deploy/headers.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { crossOriginDiscoveryPaths } from "../ai/ai-catalog.ts";
|
|
1
2
|
import {
|
|
2
3
|
API_CATALOG_PATH,
|
|
3
4
|
API_CATALOG_TYPE,
|
|
@@ -95,6 +96,11 @@ export const buildNetlifyHeaders = (
|
|
|
95
96
|
`${deployBase}${SIGNATURES_DIRECTORY_PATH}\n Content-Type: ${SIGNATURES_DIRECTORY_TYPE}`
|
|
96
97
|
);
|
|
97
98
|
}
|
|
99
|
+
// Agent registries fetch the discovery documents cross-origin; a static
|
|
100
|
+
// host sends no CORS header unless told to.
|
|
101
|
+
for (const path of crossOriginDiscoveryPaths(config)) {
|
|
102
|
+
rules.push(`${deployBase}${path}\n Access-Control-Allow-Origin: *`);
|
|
103
|
+
}
|
|
98
104
|
// Published skills live at the deployment base, outside `basePath` — the
|
|
99
105
|
// `.md` charset rule above misses them whenever a basePath is set, and the
|
|
100
106
|
// RFC wants archives served as application/gzip explicitly.
|
|
@@ -235,6 +235,26 @@ export const buildNegotiationRoutes = (
|
|
|
235
235
|
/** The `src` of the injected homepage `Link` header route. */
|
|
236
236
|
const HOME_SRC = "^/$";
|
|
237
237
|
|
|
238
|
+
const ALLOW_ANY_ORIGIN = "*";
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A main-phase `continue` route stamping `Access-Control-Allow-Origin: *` on
|
|
242
|
+
* one static discovery document (see `crossOriginDiscoveryPaths`), so a
|
|
243
|
+
* registry reading it from another origin isn't refused by the browser.
|
|
244
|
+
*/
|
|
245
|
+
const corsRoute = (path: string): VercelRoute => ({
|
|
246
|
+
continue: true,
|
|
247
|
+
headers: { "access-control-allow-origin": ALLOW_ANY_ORIGIN },
|
|
248
|
+
src: `^${routePattern(path)}$`,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
/** Whether a route is a `corsRoute` — the same three-field shape test as the others. */
|
|
252
|
+
const isCorsRoute = (route: VercelRoute): boolean =>
|
|
253
|
+
route.continue === true &&
|
|
254
|
+
route.headers?.["access-control-allow-origin"] === ALLOW_ANY_ORIGIN &&
|
|
255
|
+
isString(route.src) &&
|
|
256
|
+
Object.keys(route).length === 3;
|
|
257
|
+
|
|
238
258
|
/**
|
|
239
259
|
* Whether a route is one this module previously injected, so re-injection
|
|
240
260
|
* replaces rather than duplicates. Rewrites are identified by their `accept`
|
|
@@ -258,7 +278,8 @@ const isNegotiationRoute = (route: VercelRoute): boolean =>
|
|
|
258
278
|
(route.continue === true &&
|
|
259
279
|
isString(route.headers?.link) &&
|
|
260
280
|
route.src === HOME_SRC &&
|
|
261
|
-
Object.keys(route).length === 3)
|
|
281
|
+
Object.keys(route).length === 3) ||
|
|
282
|
+
isCorsRoute(route);
|
|
262
283
|
|
|
263
284
|
/**
|
|
264
285
|
* Splice the negotiation routes into a Build Output `config.json`, plus — when
|
|
@@ -287,7 +308,8 @@ export const injectNegotiationRoutes = (
|
|
|
287
308
|
homeLinkHeader?: string | null,
|
|
288
309
|
contentTypeOverrides?: Record<string, string>,
|
|
289
310
|
homeTokens?: number,
|
|
290
|
-
notFound: NotFoundVariants = {}
|
|
311
|
+
notFound: NotFoundVariants = {},
|
|
312
|
+
corsPaths: readonly string[] = []
|
|
291
313
|
): string | null => {
|
|
292
314
|
const overrideEntries = Object.entries(contentTypeOverrides ?? {});
|
|
293
315
|
let config: {
|
|
@@ -325,6 +347,7 @@ export const injectNegotiationRoutes = (
|
|
|
325
347
|
src: HOME_SRC,
|
|
326
348
|
});
|
|
327
349
|
}
|
|
350
|
+
headerRoutes.push(...corsPaths.map(corsRoute));
|
|
328
351
|
// Headers first: `continue` routes accumulate, so a request the rewrite
|
|
329
352
|
// route then terminates (Markdown negotiation on the homepage) still carries
|
|
330
353
|
// the Link header.
|
package/src/search/build.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import { dirname, join } from "pathe";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Build a local Pagefind search index over the built site. Pagefind indexes
|
|
@@ -6,6 +8,15 @@ import { join } from "pathe";
|
|
|
6
8
|
* `data-pagefind-ignore`, which Blume stamps on non-indexable pages
|
|
7
9
|
* (search-excluded, or hidden without the opt-in), so those stay out.
|
|
8
10
|
*
|
|
11
|
+
* The index files are fetched with `getFiles()` and written here rather than
|
|
12
|
+
* with Pagefind's `writeFiles()`: in service mode Pagefind acknowledges
|
|
13
|
+
* `writeFiles()` once tokio has buffered the writes, not once they are on
|
|
14
|
+
* disk, and `close()` kills the backend, so a loaded CI host can ship a
|
|
15
|
+
* truncated `pagefind-entry.json` (Pagefind/pagefind#1271). Writing the bytes
|
|
16
|
+
* ourselves puts them on disk before the backend goes away. Fixed upstream in
|
|
17
|
+
* Pagefind/pagefind#1272; switch back to `writeFiles()` once a release ships
|
|
18
|
+
* that fix.
|
|
19
|
+
*
|
|
9
20
|
* Returns the number of pages indexed.
|
|
10
21
|
*/
|
|
11
22
|
export const buildSearchIndex = async (outDir: string): Promise<number> => {
|
|
@@ -17,11 +28,22 @@ export const buildSearchIndex = async (outDir: string): Promise<number> => {
|
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
// These awaits are strictly ordered, not independent: the directory must be
|
|
20
|
-
// indexed before its files are
|
|
31
|
+
// indexed before its files are read, and the index closed only after.
|
|
21
32
|
// oxlint-disable-next-line react-doctor/async-parallel
|
|
22
33
|
const result = await index.addDirectory({ path: outDir });
|
|
23
|
-
|
|
34
|
+
const { files } = await index.getFiles();
|
|
24
35
|
await pagefind.close();
|
|
25
36
|
|
|
37
|
+
const searchDir = join(outDir, "pagefind");
|
|
38
|
+
const directories = new Set(
|
|
39
|
+
files.map((file) => dirname(join(searchDir, file.path)))
|
|
40
|
+
);
|
|
41
|
+
await Promise.all(
|
|
42
|
+
[...directories].map((directory) => mkdir(directory, { recursive: true }))
|
|
43
|
+
);
|
|
44
|
+
await Promise.all(
|
|
45
|
+
files.map((file) => writeFile(join(searchDir, file.path), file.content))
|
|
46
|
+
);
|
|
47
|
+
|
|
26
48
|
return result.page_count;
|
|
27
49
|
};
|
package/src/theme/entry.ts
CHANGED
|
@@ -298,6 +298,11 @@ ${THEME_MAPPING}
|
|
|
298
298
|
--tw-prose-pre-bg: var(--blume-code-background);
|
|
299
299
|
--tw-prose-th-borders: var(--blume-border);
|
|
300
300
|
--tw-prose-td-borders: var(--blume-border);
|
|
301
|
+
/* Neutralized: the plugin defaults are near-black text and shadow with no
|
|
302
|
+
dark counterpart (Blume never applies prose-invert), and the kbd rule
|
|
303
|
+
below draws the badge with Blume tokens instead. */
|
|
304
|
+
--tw-prose-kbd: inherit;
|
|
305
|
+
--tw-prose-kbd-shadows: transparent;
|
|
301
306
|
color: var(--blume-muted-foreground);
|
|
302
307
|
font-size: 0.875rem;
|
|
303
308
|
line-height: 1.7;
|
|
@@ -827,6 +832,24 @@ pre:has(.line.focused):hover .line:not(.focused) {
|
|
|
827
832
|
content: none;
|
|
828
833
|
}
|
|
829
834
|
|
|
835
|
+
/* Keyboard keys (<kbd>) render as the same bordered badge the search dialog
|
|
836
|
+
uses for its shortcut hints. Typography's own kbd rule is scoped with
|
|
837
|
+
:not(.not-prose *), so it never reaches a key inside <Steps>, <Callout>,
|
|
838
|
+
<Card>, or any other not-prose component; this rule is deliberately
|
|
839
|
+
unscoped from that exclusion so a key looks the same everywhere in the
|
|
840
|
+
content column. Unlayered, so it outranks the plugin's layered rule. */
|
|
841
|
+
.prose kbd {
|
|
842
|
+
background: var(--blume-muted);
|
|
843
|
+
border: 1px solid var(--blume-border);
|
|
844
|
+
border-radius: 0.25rem;
|
|
845
|
+
box-shadow: none;
|
|
846
|
+
color: inherit;
|
|
847
|
+
font-family: var(--font-mono);
|
|
848
|
+
font-size: 0.85em;
|
|
849
|
+
font-weight: 500;
|
|
850
|
+
padding: 0.125rem 0.25rem;
|
|
851
|
+
}
|
|
852
|
+
|
|
830
853
|
/* Inline code highlighting: Shiki colors the tokens of a \`code\`{:lang} snippet
|
|
831
854
|
via the same dual-theme CSS variables as fenced blocks, keeping the inline
|
|
832
855
|
pill background. Always on — it only fires on the trailing {:lang} marker. */
|