blume 1.5.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -0
- package/dist/cli/index.js +3639 -1377
- package/dist/cli/index.js.map +103 -91
- package/dist/types/ai/component-markdown.d.ts +79 -0
- package/dist/types/components/layout/nav-utils.d.ts +60 -0
- package/dist/types/core/base-path.d.ts +9 -0
- package/dist/types/core/config-input.d.ts +206 -4
- package/dist/types/core/config.d.ts +6 -4
- package/dist/types/core/data.d.ts +23 -1
- package/dist/types/core/github.d.ts +35 -0
- package/dist/types/core/i18n-ui.d.ts +8 -0
- package/dist/types/core/navigation.d.ts +69 -0
- package/dist/types/core/schema.d.ts +117 -1
- package/dist/types/core/sources/types.d.ts +31 -6
- package/dist/types/core/types.d.ts +23 -2
- package/dist/types/markdown/features.d.ts +21 -0
- package/dist/types/openapi/references.d.ts +21 -1
- package/dist/types/seo/jsonld.d.ts +105 -0
- package/dist/types/theme/fonts.d.ts +34 -4
- package/docs/_snippets/include-demo.mdx +7 -0
- package/docs/advanced/api-reference.mdx +3 -3
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/graphql.mdx +84 -0
- package/docs/advanced/meta.ts +8 -1
- package/docs/configuration/ai.mdx +21 -3
- package/docs/configuration/index.mdx +24 -0
- package/docs/configuration/search.mdx +13 -1
- package/docs/configuration/seo.mdx +27 -0
- package/docs/configuration/theming.mdx +17 -0
- package/docs/content/components.mdx +7 -0
- package/docs/content/includes.mdx +68 -0
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +25 -0
- package/docs/content/sources.mdx +42 -1
- package/docs/content/syntax.mdx +69 -1
- package/docs/content/versioning.mdx +15 -9
- package/docs/reference/cli.mdx +2 -1
- package/package.json +23 -14
- package/skills/blume-migrate/SKILL.md +16 -7
- package/skills/blume-migrate/references/docusaurus.md +5 -3
- package/skills/blume-migrate/references/fumadocs.md +10 -2
- package/skills/blume-migrate/references/mintlify.md +3 -2
- package/skills/blume-migrate/references/nextra.md +2 -2
- package/skills/blume-migrate/references/starlight.md +1 -1
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/ask-data.ts +2 -1
- package/src/ai/component-markdown.ts +199 -36
- package/src/ai/llms.ts +93 -6
- package/src/ai/markdown.ts +2 -2
- package/src/ai/mcp/discovery.ts +10 -2
- package/src/ai/mcp/server.ts +74 -2
- package/src/astro/generate.ts +183 -116
- package/src/astro/include-hmr.ts +81 -0
- package/src/astro/include-refresh.ts +0 -0
- package/src/astro/index.ts +3 -5
- package/src/astro/templates.ts +125 -76
- package/src/cli/commands/build.ts +84 -15
- package/src/cli/init/questions.ts +1 -0
- package/src/cli/init/scaffold.ts +27 -4
- package/src/components/colors.ts +142 -0
- package/src/components/content/Badge.astro +5 -12
- package/src/components/content/Callout.astro +19 -36
- package/src/components/content/Card.astro +15 -21
- package/src/components/content/Component.astro +10 -1
- package/src/components/content/GithubInfo.astro +28 -9
- package/src/components/content/Tabs.astro +27 -5
- package/src/components/content/github-info.ts +20 -5
- package/src/components/dropdown-dismiss.ts +122 -0
- package/src/components/layout/Fonts.astro +15 -8
- package/src/components/layout/Header.astro +44 -0
- package/src/components/layout/LanguageSwitcher.astro +9 -1
- package/src/components/layout/NavSelector.astro +12 -3
- package/src/components/layout/NavTree.astro +6 -18
- package/src/components/layout/PageActions.astro +29 -8
- package/src/components/layout/PageLayout.astro +10 -1
- package/src/components/layout/ReferenceLayout.astro +6 -1
- package/src/components/layout/RootLayout.astro +46 -15
- package/src/components/layout/Search.astro +36 -4
- package/src/components/layout/TableOfContents.astro +8 -2
- package/src/components/layout/head-scripts.ts +53 -1
- package/src/components/openapi/ApiOverview.astro +13 -3
- package/src/components/openapi/AsyncApiOperation.astro +7 -14
- package/src/components/openapi/GraphqlChip.astro +33 -0
- package/src/components/openapi/GraphqlFieldsTable.astro +111 -0
- package/src/components/openapi/GraphqlOperation.astro +186 -0
- package/src/components/openapi/GraphqlType.astro +154 -0
- package/src/components/openapi/MethodBadge.astro +3 -14
- package/src/components/openapi/Operation.astro +12 -5
- package/src/components/openapi/OperationPanel.astro +43 -0
- package/src/components/openapi/RequestPanel.astro +5 -10
- package/src/components/openapi/Responses.astro +1 -16
- package/src/components/openapi/graphql-helpers.ts +466 -0
- package/src/components/openapi/playground-client.ts +15 -0
- package/src/components/openapi/sample-panels.ts +45 -0
- package/src/components/openapi/snippets.ts +13 -35
- package/src/core/base-path.ts +11 -0
- package/src/core/config-input.ts +209 -2
- package/src/core/config.ts +6 -4
- package/src/core/content-assets.ts +15 -4
- package/src/core/data.ts +18 -2
- package/src/core/diagnostics.ts +8 -0
- package/src/core/frontmatter.ts +20 -8
- package/src/core/github.ts +71 -0
- package/src/core/graph.ts +22 -8
- package/src/core/heading-markers.ts +96 -0
- package/src/core/i18n-ui.ts +11 -0
- package/src/core/includes.ts +632 -0
- package/src/core/last-modified.ts +36 -11
- package/src/core/links.ts +79 -13
- package/src/core/meta.ts +2 -1
- package/src/core/nav-diagnostics.ts +11 -2
- package/src/core/navigation.ts +27 -6
- package/src/core/project-graph.ts +61 -9
- package/src/core/schema.ts +226 -35
- package/src/core/server-features.ts +5 -9
- package/src/core/sources/github-releases.ts +2 -2
- package/src/core/sources/normalize.ts +502 -115
- package/src/core/sources/notion.ts +43 -8
- package/src/core/sources/obsidian.ts +1038 -0
- package/src/core/sources/read.ts +36 -1
- package/src/core/sources/resolve.ts +34 -1
- package/src/core/sources/types.ts +28 -6
- package/src/core/sources/watch.ts +12 -8
- package/src/core/tsconfig-aliases.ts +48 -35
- package/src/core/types.ts +25 -2
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +2 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +2 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +2 -0
- package/src/core/ui-packs/el.ts +2 -0
- package/src/core/ui-packs/es.ts +2 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +2 -0
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +2 -0
- package/src/core/ui-packs/hu.ts +2 -0
- package/src/core/ui-packs/id.ts +2 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +2 -0
- package/src/core/ui-packs/ko.ts +2 -0
- package/src/core/ui-packs/nl.ts +2 -0
- package/src/core/ui-packs/no.ts +2 -0
- package/src/core/ui-packs/pl.ts +2 -0
- package/src/core/ui-packs/pt-br.ts +2 -0
- package/src/core/ui-packs/pt.ts +2 -0
- package/src/core/ui-packs/ro.ts +2 -0
- package/src/core/ui-packs/ru.ts +2 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +2 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +2 -0
- package/src/core/ui-packs/uk.ts +2 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/core/version-cut.ts +21 -3
- package/src/core/yaml.ts +26 -0
- package/src/deploy/function-bundle.ts +251 -0
- package/src/eval/schema.ts +3 -1
- package/src/markdown/code-title.ts +22 -16
- package/src/markdown/features.ts +21 -0
- package/src/markdown/fence-meta.ts +50 -0
- package/src/markdown/heading-anchors.ts +198 -37
- package/src/markdown/include.ts +247 -0
- package/src/markdown/index.ts +43 -34
- package/src/markdown/language-icon.ts +2 -2
- package/src/markdown/mdast.ts +7 -3
- package/src/markdown/ts2js.ts +264 -0
- package/src/openapi/asyncapi.ts +4 -1
- package/src/openapi/graphql-build.ts +293 -0
- package/src/openapi/graphql.ts +212 -0
- package/src/openapi/model.ts +38 -5
- package/src/openapi/parse.ts +34 -0
- package/src/openapi/proxy.ts +30 -5
- package/src/openapi/references.ts +89 -13
- package/src/openapi/render-mdx.ts +48 -8
- package/src/openapi/scalar.ts +5 -12
- package/src/openapi/source.ts +91 -23
- package/src/registry/eject.ts +11 -0
- package/src/search/documents.ts +229 -37
- package/src/search/orama-index.ts +9 -5
- package/src/seo/jsonld.ts +293 -51
- package/src/theme/code-block-padding.ts +16 -0
- package/src/theme/entry.ts +65 -11
- package/src/theme/fonts.ts +189 -16
- package/src/translate/prompts.ts +2 -0
- package/src/translate/run.ts +7 -0
- package/src/translate/work-list.ts +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Light-dismiss for the site's `<details>`-based dropdowns: the page actions
|
|
3
|
+
* (Export / Open in chat / Connect to MCP), the header language switcher and
|
|
4
|
+
* the nav selectors all render the same floating `<details>` + absolute panel,
|
|
5
|
+
* and a native `<details>` only closes when its own `<summary>` is clicked
|
|
6
|
+
* again, leaving the panel hanging over the page. Any dropdown that opts in
|
|
7
|
+
* with `data-blume-dropdown` gets the four legs of a real menu:
|
|
8
|
+
*
|
|
9
|
+
* - a pointer press outside the open panel closes it;
|
|
10
|
+
* - Escape closes it, restoring focus to the trigger when the keypress
|
|
11
|
+
* originated inside the panel;
|
|
12
|
+
* - keyboard focus leaving the panel closes it;
|
|
13
|
+
* - the window losing focus closes it — the only signal the parent document
|
|
14
|
+
* gets when a press lands inside an `<iframe>` embed, since pointer events
|
|
15
|
+
* in a child browsing context never propagate up.
|
|
16
|
+
*
|
|
17
|
+
* Every listener sits on `document`/`window` once per real page load and
|
|
18
|
+
* queries the DOM live, so client-router swaps (which rebuild the dropdown
|
|
19
|
+
* markup) need no re-init and never stack duplicate listeners. Per-component
|
|
20
|
+
* behavior — one-open-at-a-time within a group, viewport flipping — stays in
|
|
21
|
+
* the component that owns it.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const OPEN_DROPDOWN = "details[data-blume-dropdown][open]";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Every dropdown currently open. Each component group keeps itself to one
|
|
28
|
+
* open panel, but a page holds several groups (the header selectors and the
|
|
29
|
+
* page actions), and a browser that doesn't focus a `<summary>` on click lets
|
|
30
|
+
* a keyboard-opened panel join a pointer-opened one — so the dismissal rules
|
|
31
|
+
* apply to all of them, never just the first in DOM order.
|
|
32
|
+
*/
|
|
33
|
+
const openDropdowns = (): HTMLDetailsElement[] => [
|
|
34
|
+
...document.querySelectorAll<HTMLDetailsElement>(OPEN_DROPDOWN),
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Close `open`. `restoreFocus` moves focus back to its trigger, which a
|
|
39
|
+
* keyboard dismissal wants and a pointer or focus-driven one does not.
|
|
40
|
+
*/
|
|
41
|
+
const dismiss = (open: HTMLDetailsElement, restoreFocus: boolean): void => {
|
|
42
|
+
open.open = false;
|
|
43
|
+
if (restoreFocus) {
|
|
44
|
+
open.querySelector<HTMLElement>("summary")?.focus();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const isInside = (
|
|
49
|
+
dropdown: HTMLDetailsElement,
|
|
50
|
+
target: EventTarget | null
|
|
51
|
+
): boolean => target instanceof Node && dropdown.contains(target);
|
|
52
|
+
|
|
53
|
+
const onPointerDown = (event: PointerEvent): void => {
|
|
54
|
+
for (const open of openDropdowns()) {
|
|
55
|
+
if (!isInside(open, event.target)) {
|
|
56
|
+
dismiss(open, false);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const onKeyDown = (event: KeyboardEvent): void => {
|
|
62
|
+
// `isComposing`: an IME cancel arrives as Escape and isn't a dismissal.
|
|
63
|
+
if (event.key !== "Escape" || event.isComposing) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// An Escape aimed at a modal surface stacked on top (the search dialog
|
|
67
|
+
// traps focus inside itself) dismisses that surface only — the same guard
|
|
68
|
+
// the Ask panel applies. Everything outside the modal is inert, so a focus
|
|
69
|
+
// restore here could not land anyway.
|
|
70
|
+
if (event.target instanceof Element && event.target.closest("dialog")) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Only a keypress that originated inside a panel gets its focus returned
|
|
74
|
+
// to that trigger; yanking focus from an unrelated control the user had
|
|
75
|
+
// moved on to would be a surprise.
|
|
76
|
+
for (const open of openDropdowns()) {
|
|
77
|
+
dismiss(open, isInside(open, event.target));
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const onFocusOut = (event: FocusEvent): void => {
|
|
82
|
+
// Only focus *leaving a panel* counts: a move between two unrelated controls
|
|
83
|
+
// (a dialog handing focus back to its trigger, say) must not close a panel
|
|
84
|
+
// that never held focus. And a null `relatedTarget` means focus went
|
|
85
|
+
// nowhere focusable (a click on plain content, the panel being hidden, the
|
|
86
|
+
// window blurring) — the pointer and blur legs own those, and closing here
|
|
87
|
+
// would hide a panel item before its own click lands in browsers that
|
|
88
|
+
// don't focus buttons on press.
|
|
89
|
+
const { relatedTarget } = event;
|
|
90
|
+
if (!(relatedTarget instanceof Node)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const open of openDropdowns()) {
|
|
94
|
+
if (isInside(open, event.target) && !open.contains(relatedTarget)) {
|
|
95
|
+
dismiss(open, false);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const onWindowBlur = (): void => {
|
|
101
|
+
for (const open of openDropdowns()) {
|
|
102
|
+
dismiss(open, false);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
let installed = false;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Register the document/window listeners. Idempotent: every component that
|
|
110
|
+
* renders a dropdown calls this from its own script, and a page may render
|
|
111
|
+
* several of them.
|
|
112
|
+
*/
|
|
113
|
+
export const installDropdownDismiss = (): void => {
|
|
114
|
+
if (installed) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
installed = true;
|
|
118
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
119
|
+
document.addEventListener("keydown", onKeyDown);
|
|
120
|
+
document.addEventListener("focusout", onFocusOut);
|
|
121
|
+
window.addEventListener("blur", onWindowBlur);
|
|
122
|
+
};
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Emits the optimized @font-face declarations + preload links for each
|
|
3
3
|
// configured font (Astro's Fonts API). Renders nothing when no fonts are set;
|
|
4
4
|
// the CSS variables match the astro.config `fonts:` entries. Preloads are
|
|
5
|
-
// narrowed to the weights above-the-fold text renders in
|
|
5
|
+
// narrowed to the weights above-the-fold text renders in and, for providers
|
|
6
|
+
// that split faces by subset, to the subsets the site's locales need (see
|
|
6
7
|
// `theme/fonts.ts`); a bare string entry — an older generated template — keeps
|
|
7
8
|
// the previous preload-everything behavior.
|
|
8
9
|
import { Font } from "astro:assets";
|
|
@@ -14,16 +15,22 @@ interface Props {
|
|
|
14
15
|
|
|
15
16
|
const { cssVars } = Astro.props;
|
|
16
17
|
|
|
18
|
+
/** The preload filters for one head entry: every weight × every subset. */
|
|
19
|
+
const preloadFilters = (head: FontHead) =>
|
|
20
|
+
head.preloadWeights.flatMap((weight) =>
|
|
21
|
+
head.preloadSubsets
|
|
22
|
+
? head.preloadSubsets.map((subset) => ({
|
|
23
|
+
style: "normal" as const,
|
|
24
|
+
subset,
|
|
25
|
+
weight,
|
|
26
|
+
}))
|
|
27
|
+
: [{ style: "normal" as const, weight }]
|
|
28
|
+
);
|
|
29
|
+
|
|
17
30
|
const entries = cssVars.map((value) =>
|
|
18
31
|
typeof value === "string"
|
|
19
32
|
? { cssVariable: value, preload: true as const }
|
|
20
|
-
: {
|
|
21
|
-
cssVariable: value.cssVariable,
|
|
22
|
-
preload: value.preloadWeights.map((weight) => ({
|
|
23
|
-
style: "normal" as const,
|
|
24
|
-
weight,
|
|
25
|
-
})),
|
|
26
|
-
}
|
|
33
|
+
: { cssVariable: value.cssVariable, preload: preloadFilters(value) }
|
|
27
34
|
);
|
|
28
35
|
---
|
|
29
36
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import Ask from "blume:ask";
|
|
3
3
|
import data from "blume:data";
|
|
4
4
|
import { withBase } from "../islands/base-path.ts";
|
|
5
|
+
import { isExternalUrl } from "../../core/base-path.ts";
|
|
5
6
|
import type { ComponentOverride } from "../../core/define-components.ts";
|
|
6
7
|
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
7
8
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
@@ -120,6 +121,19 @@ const tabsNavClass = hasSidebar
|
|
|
120
121
|
const iconButton =
|
|
121
122
|
"inline-flex size-9 cursor-pointer items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground";
|
|
122
123
|
|
|
124
|
+
// Header links: plain actions are secondary and hide below `sm`, where the
|
|
125
|
+
// header has room for the logo and the drawer toggle and nothing else. The one
|
|
126
|
+
// call to action hides there too — but only when a drawer toggle is actually
|
|
127
|
+
// competing for the space. A page with no drawer (a PageLayout landing page
|
|
128
|
+
// with no tabs) has nowhere else to surface it, so it stays. `withBase` passes
|
|
129
|
+
// external, protocol-relative and fragment hrefs through untouched.
|
|
130
|
+
const actionClass =
|
|
131
|
+
"hidden rounded-full px-3 py-1.5 font-medium text-muted-foreground text-sm transition-colors hover:text-foreground sm:inline-flex";
|
|
132
|
+
const ctaClass = [
|
|
133
|
+
showNavToggle ? "hidden sm:inline-flex" : "inline-flex",
|
|
134
|
+
"items-center gap-1 rounded-full bg-accent px-3.5 py-1.5 font-medium text-accent-foreground text-sm transition-opacity hover:opacity-90",
|
|
135
|
+
].join(" ");
|
|
136
|
+
|
|
123
137
|
// Delegated handlers for the theme toggle, mobile nav drawer, and banner
|
|
124
138
|
// dismiss. Living with the header means every page that renders it — docs pages
|
|
125
139
|
// and custom pages alike — gets identical behavior from one source.
|
|
@@ -212,6 +226,36 @@ const clickScript = `(()=>{const dr=()=>{const h=document.querySelector("[data-b
|
|
|
212
226
|
)
|
|
213
227
|
}
|
|
214
228
|
<div class="flex items-center gap-2">
|
|
229
|
+
{
|
|
230
|
+
/* Plain links first, then the one filled button: the reader's eye lands
|
|
231
|
+
on the button because nothing beside it competes. */
|
|
232
|
+
(navigation.actions ?? []).map((action) => {
|
|
233
|
+
const external = isExternalUrl(action.href);
|
|
234
|
+
return (
|
|
235
|
+
<a
|
|
236
|
+
class={actionClass}
|
|
237
|
+
href={withBase(action.href)}
|
|
238
|
+
rel={external ? "noreferrer" : undefined}
|
|
239
|
+
target={external ? "_blank" : undefined}
|
|
240
|
+
>
|
|
241
|
+
{action.label}
|
|
242
|
+
</a>
|
|
243
|
+
);
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
{
|
|
247
|
+
navigation.cta && (
|
|
248
|
+
<a
|
|
249
|
+
class={ctaClass}
|
|
250
|
+
href={withBase(navigation.cta.href)}
|
|
251
|
+
rel={isExternalUrl(navigation.cta.href) ? "noreferrer" : undefined}
|
|
252
|
+
target={isExternalUrl(navigation.cta.href) ? "_blank" : undefined}
|
|
253
|
+
>
|
|
254
|
+
{navigation.cta.label}
|
|
255
|
+
<Icon name="chevron-right" size={14} />
|
|
256
|
+
</a>
|
|
257
|
+
)
|
|
258
|
+
}
|
|
215
259
|
{
|
|
216
260
|
navigation.repoUrl && (
|
|
217
261
|
<a
|
|
@@ -20,7 +20,7 @@ const menuRowClass =
|
|
|
20
20
|
|
|
21
21
|
{
|
|
22
22
|
options.length > 1 && (
|
|
23
|
-
<details class="group relative">
|
|
23
|
+
<details class="group relative" data-blume-dropdown>
|
|
24
24
|
<summary
|
|
25
25
|
aria-label={label}
|
|
26
26
|
class={`${iconButton} list-none [&::-webkit-details-marker]:hidden`}
|
|
@@ -56,3 +56,11 @@ const menuRowClass =
|
|
|
56
56
|
</details>
|
|
57
57
|
)
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
<script>
|
|
61
|
+
import { installDropdownDismiss } from "../dropdown-dismiss.ts";
|
|
62
|
+
|
|
63
|
+
// Outside-click / Escape / focus-out dismissal, shared with the nav
|
|
64
|
+
// selectors and the page actions.
|
|
65
|
+
installDropdownDismiss();
|
|
66
|
+
</script>
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import { withBase } from "../islands/base-path.ts";
|
|
3
3
|
// A top-level navigation selector: a dropdown that switches
|
|
4
4
|
// between partitions of the site — a product, a version, or any grouped set of
|
|
5
|
-
// destinations (`navigation.selectors` in the config).
|
|
6
|
-
// <details>/<summary> like the language switcher
|
|
5
|
+
// destinations (`navigation.selectors` in the config). Built on
|
|
6
|
+
// <details>/<summary> like the language switcher; the only script is the
|
|
7
|
+
// shared light-dismiss so an open panel doesn't linger over the page.
|
|
7
8
|
import type { NavSelector } from "../../core/types.ts";
|
|
8
9
|
import Icon from "../Icon.astro";
|
|
9
10
|
import { isUnderPath } from "../../core/navigation.ts";
|
|
@@ -40,7 +41,7 @@ const menuRowClass =
|
|
|
40
41
|
|
|
41
42
|
{
|
|
42
43
|
selector.items.length > 0 && (
|
|
43
|
-
<details class="group relative">
|
|
44
|
+
<details class="group relative" data-blume-dropdown>
|
|
44
45
|
<summary
|
|
45
46
|
aria-label={selector.label}
|
|
46
47
|
class={`${iconButton} list-none [&::-webkit-details-marker]:hidden`}
|
|
@@ -85,3 +86,11 @@ const menuRowClass =
|
|
|
85
86
|
</details>
|
|
86
87
|
)
|
|
87
88
|
}
|
|
89
|
+
|
|
90
|
+
<script>
|
|
91
|
+
import { installDropdownDismiss } from "../dropdown-dismiss.ts";
|
|
92
|
+
|
|
93
|
+
// Outside-click / Escape / focus-out dismissal, shared with the language
|
|
94
|
+
// switcher and the page actions.
|
|
95
|
+
installDropdownDismiss();
|
|
96
|
+
</script>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
3
3
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
4
4
|
import type { NavNode } from "../../core/types.ts";
|
|
5
|
+
import { methodColor } from "../colors.ts";
|
|
5
6
|
import Icon from "../Icon.astro";
|
|
6
7
|
import { withBase } from "../islands/base-path.ts";
|
|
7
8
|
import Self from "./NavTree.astro";
|
|
@@ -44,24 +45,11 @@ const n = { ...EN_UI.nav, ...strings };
|
|
|
44
45
|
|
|
45
46
|
const badgeBase =
|
|
46
47
|
"shrink-0 rounded-full px-1.5 py-0.5 font-medium text-[0.65rem] leading-none";
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
GET: "bg-green-500/15 text-green-700 dark:text-green-300",
|
|
53
|
-
HEAD: "bg-muted text-muted-foreground",
|
|
54
|
-
OPTIONS: "bg-muted text-muted-foreground",
|
|
55
|
-
PATCH: "bg-yellow-500/20 text-yellow-800 dark:text-yellow-300",
|
|
56
|
-
POST: "bg-blue-500/15 text-blue-700 dark:text-blue-300",
|
|
57
|
-
PUT: "bg-orange-500/15 text-orange-700 dark:text-orange-300",
|
|
58
|
-
RECEIVE: "bg-teal-500/15 text-teal-700 dark:text-teal-300",
|
|
59
|
-
SEND: "bg-violet-500/15 text-violet-700 dark:text-violet-300",
|
|
60
|
-
};
|
|
61
|
-
const badgeClassFor = (badge: string): string => {
|
|
62
|
-
const method = METHOD_BADGE[badge.toUpperCase()];
|
|
63
|
-
return method ? `${badgeBase} ${method}` : badgeClass;
|
|
64
|
-
};
|
|
48
|
+
// HTTP-method, AsyncAPI-action, and GraphQL operation-kind badges (from a
|
|
49
|
+
// reference's sidebar) share MethodBadge's table; every other badge falls
|
|
50
|
+
// through to the same neutral style.
|
|
51
|
+
const badgeClassFor = (badge: string): string =>
|
|
52
|
+
`${badgeBase} ${methodColor(badge)}`;
|
|
65
53
|
const deprecatedClass =
|
|
66
54
|
"shrink-0 rounded-full bg-amber-500/10 px-1.5 py-0.5 font-medium text-[0.65rem] text-amber-700 leading-none dark:text-amber-300";
|
|
67
55
|
|
|
@@ -24,6 +24,12 @@ interface Props {
|
|
|
24
24
|
openInChat?: readonly OpenInChatProvider[];
|
|
25
25
|
/** Localized action labels. */
|
|
26
26
|
strings?: UIStrings["actions"];
|
|
27
|
+
/**
|
|
28
|
+
* Draw the top rule and spacing that separate the block from the table of
|
|
29
|
+
* contents above it. The rail passes `false` when no contents rendered, so
|
|
30
|
+
* the block does not open an otherwise empty column with a divider.
|
|
31
|
+
*/
|
|
32
|
+
divider?: boolean;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
const {
|
|
@@ -35,6 +41,7 @@ const {
|
|
|
35
41
|
mcpName,
|
|
36
42
|
openInChat,
|
|
37
43
|
strings,
|
|
44
|
+
divider = true,
|
|
38
45
|
} = Astro.props;
|
|
39
46
|
// Merge over the English defaults so a label missing from a translation (or from
|
|
40
47
|
// a not-yet-regenerated snapshot) still renders instead of coming out blank.
|
|
@@ -93,8 +100,9 @@ const menuRowClass =
|
|
|
93
100
|
---
|
|
94
101
|
|
|
95
102
|
<div
|
|
96
|
-
class="
|
|
103
|
+
class:list={["space-y-0.5", divider && "mt-8 border-border border-t pt-4"]}
|
|
97
104
|
data-blume-page-actions
|
|
105
|
+
data-i18n-chat-prompt={a.openInChatPrompt}
|
|
98
106
|
data-i18n-copied={a.copied}
|
|
99
107
|
data-i18n-generating={a.generating}
|
|
100
108
|
data-mcp-name={mcpName ?? undefined}
|
|
@@ -122,7 +130,7 @@ const menuRowClass =
|
|
|
122
130
|
|
|
123
131
|
{
|
|
124
132
|
(exportPdf || exportEpub) && (
|
|
125
|
-
<details class="group relative">
|
|
133
|
+
<details class="group relative" data-blume-dropdown>
|
|
126
134
|
<summary
|
|
127
135
|
class={`${rowClass} cursor-pointer list-none [&::-webkit-details-marker]:hidden`}
|
|
128
136
|
>
|
|
@@ -159,7 +167,7 @@ const menuRowClass =
|
|
|
159
167
|
|
|
160
168
|
{
|
|
161
169
|
providers.length > 0 && (
|
|
162
|
-
<details class="group relative">
|
|
170
|
+
<details class="group relative" data-blume-dropdown>
|
|
163
171
|
<summary
|
|
164
172
|
class={`${rowClass} cursor-pointer list-none [&::-webkit-details-marker]:hidden`}
|
|
165
173
|
>
|
|
@@ -199,7 +207,7 @@ const menuRowClass =
|
|
|
199
207
|
|
|
200
208
|
{
|
|
201
209
|
mcpUrl && (
|
|
202
|
-
<details class="group relative">
|
|
210
|
+
<details class="group relative" data-blume-dropdown>
|
|
203
211
|
<summary
|
|
204
212
|
class={`${rowClass} cursor-pointer list-none [&::-webkit-details-marker]:hidden`}
|
|
205
213
|
>
|
|
@@ -252,6 +260,7 @@ const menuRowClass =
|
|
|
252
260
|
|
|
253
261
|
<script>
|
|
254
262
|
import { copyText, flashLabel } from "../copy-feedback.ts";
|
|
263
|
+
import { installDropdownDismiss } from "../dropdown-dismiss.ts";
|
|
255
264
|
import { prefixBase } from "../islands/base-path.ts";
|
|
256
265
|
import { rafThrottle } from "../raf-throttle.ts";
|
|
257
266
|
|
|
@@ -329,6 +338,11 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
329
338
|
menu.classList.toggle("mb-1", flipUp);
|
|
330
339
|
};
|
|
331
340
|
|
|
341
|
+
const openDropdown = () =>
|
|
342
|
+
document.querySelector<HTMLDetailsElement>(
|
|
343
|
+
"[data-blume-page-actions] details[open]"
|
|
344
|
+
);
|
|
345
|
+
|
|
332
346
|
// rAF-coalesced so a live resize drag re-reads layout once per frame, not
|
|
333
347
|
// once per event, while the open menu still tracks the viewport. Registered
|
|
334
348
|
// once at module scope (this bundle runs once per real page load) and
|
|
@@ -336,15 +350,17 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
336
350
|
window.addEventListener(
|
|
337
351
|
"resize",
|
|
338
352
|
rafThrottle(() => {
|
|
339
|
-
const open =
|
|
340
|
-
"[data-blume-page-actions] details[open]"
|
|
341
|
-
);
|
|
353
|
+
const open = openDropdown();
|
|
342
354
|
if (open) {
|
|
343
355
|
placeMenu(open);
|
|
344
356
|
}
|
|
345
357
|
})
|
|
346
358
|
);
|
|
347
359
|
|
|
360
|
+
// Outside-click / Escape / focus-out dismissal is the shared <details>
|
|
361
|
+
// dropdown behavior (also the header language switcher and nav selectors).
|
|
362
|
+
installDropdownDismiss();
|
|
363
|
+
|
|
348
364
|
// Per-page setup, run on the initial load and again after every
|
|
349
365
|
// client-router swap: the swap rebuilds the actions menu from
|
|
350
366
|
// server-rendered markup, so every handler below binds to fresh elements.
|
|
@@ -379,9 +395,14 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
379
395
|
const copiedLabel = root.getAttribute("data-i18n-copied") || "Copied!";
|
|
380
396
|
const generatingLabel =
|
|
381
397
|
root.getAttribute("data-i18n-generating") || "Generating…";
|
|
398
|
+
// Always stamped: the frontmatter merges `EN_UI.actions` under any
|
|
399
|
+
// override, so the English default lives only in core/i18n-ui.ts.
|
|
400
|
+
const promptTemplate = root.getAttribute("data-i18n-chat-prompt") ?? "";
|
|
382
401
|
const absolute = new URL(md, location.origin).href;
|
|
402
|
+
// Replacer function, not a string: a URL may contain `$`, which a string
|
|
403
|
+
// replacement would interpret as a substitution pattern.
|
|
383
404
|
const query = encodeURIComponent(
|
|
384
|
-
|
|
405
|
+
promptTemplate.replaceAll("{url}", () => absolute)
|
|
385
406
|
);
|
|
386
407
|
|
|
387
408
|
for (const link of root.querySelectorAll("[data-open-in]")) {
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
BlumeFavicon,
|
|
23
23
|
BlumeLogo,
|
|
24
24
|
} from "../../core/data.ts";
|
|
25
|
+
import data from "blume:data";
|
|
25
26
|
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
26
27
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
27
28
|
import type { FontHead } from "../../theme/fonts.ts";
|
|
@@ -40,7 +41,11 @@ import Analytics from "./Analytics.astro";
|
|
|
40
41
|
import Banner from "./Banner.astro";
|
|
41
42
|
import Favicon from "./Favicon.astro";
|
|
42
43
|
import Fonts from "./Fonts.astro";
|
|
43
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
BANNER_INIT_SCRIPT,
|
|
46
|
+
SWAP_STYLESHEET_INIT_SCRIPT,
|
|
47
|
+
THEME_INIT_SCRIPT,
|
|
48
|
+
} from "./head-scripts.ts";
|
|
44
49
|
import Header from "./Header.astro";
|
|
45
50
|
import { currentTabForRoute } from "./nav-utils.ts";
|
|
46
51
|
import WebMcp from "./WebMcp.astro";
|
|
@@ -213,6 +218,7 @@ const structuredData =
|
|
|
213
218
|
base: import.meta.env.BASE_URL,
|
|
214
219
|
breadcrumbs: [],
|
|
215
220
|
description,
|
|
221
|
+
identity: data.config.identity,
|
|
216
222
|
locale,
|
|
217
223
|
route,
|
|
218
224
|
siteName: site.title,
|
|
@@ -235,6 +241,9 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
235
241
|
swap the DOM in place instead of tearing the document down, so no
|
|
236
242
|
browser paints a blank frame between pages. */}
|
|
237
243
|
<ClientRouter />
|
|
244
|
+
{/* Loads a next page's body-hoisted CSS before the router swaps it in —
|
|
245
|
+
see SWAP_STYLESHEET_INIT_SCRIPT for the unstyled-frame failure mode. */}
|
|
246
|
+
<script is:inline set:html={SWAP_STYLESHEET_INIT_SCRIPT} />
|
|
238
247
|
<title>{pageTitle}</title>
|
|
239
248
|
<Favicon appleIcon={appleIcon} favicon={favicon} />
|
|
240
249
|
<Fonts cssVars={fontCssVars ?? []} />
|
|
@@ -10,7 +10,11 @@ import WebMcp from "./WebMcp.astro";
|
|
|
10
10
|
import Banner from "./Banner.astro";
|
|
11
11
|
import Favicon from "./Favicon.astro";
|
|
12
12
|
import Fonts from "./Fonts.astro";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BANNER_INIT_SCRIPT,
|
|
15
|
+
SCALAR_THEME_INIT_SCRIPT,
|
|
16
|
+
THEME_INIT_SCRIPT,
|
|
17
|
+
} from "./head-scripts.ts";
|
|
14
18
|
import Header from "./Header.astro";
|
|
15
19
|
|
|
16
20
|
// A minimal shell for the Scalar API/AsyncAPI reference: Blume's banner + navbar
|
|
@@ -129,6 +133,7 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
129
133
|
id="blume-content"
|
|
130
134
|
>
|
|
131
135
|
<slot />
|
|
136
|
+
<script is:inline set:html={SCALAR_THEME_INIT_SCRIPT} />
|
|
132
137
|
</div>
|
|
133
138
|
<WebMcp />
|
|
134
139
|
</body>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
+
import data from "blume:data";
|
|
2
3
|
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
3
4
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
4
5
|
import { resolveDateFormatOptions } from "../../core/date-format.ts";
|
|
@@ -12,6 +13,15 @@ import type {
|
|
|
12
13
|
NavSelector as NavSelectorType,
|
|
13
14
|
} from "../../core/types.ts";
|
|
14
15
|
import "blume:theme";
|
|
16
|
+
// medium-zoom's default build injects its stylesheet into <head> as a bare
|
|
17
|
+
// <style> the moment the module evaluates — once per document. The client
|
|
18
|
+
// router's head swap drops every non-persisted head element on the first
|
|
19
|
+
// navigation, and the cached module never re-injects, so the zoom cursor
|
|
20
|
+
// vanished and `close()` hung forever waiting for a `transitionend` its
|
|
21
|
+
// (now absent) transition rule never fires. Importing the stylesheet here
|
|
22
|
+
// puts it in the page CSS the router carries across swaps; the script below
|
|
23
|
+
// loads the `pure` build, which ships no injection.
|
|
24
|
+
import "medium-zoom/dist/style.css";
|
|
15
25
|
import { ClientRouter } from "astro:transitions";
|
|
16
26
|
import type { FontHead } from "../../theme/fonts.ts";
|
|
17
27
|
import type { ComponentOverride } from "../../core/define-components.ts";
|
|
@@ -23,6 +33,7 @@ import {
|
|
|
23
33
|
import { buildStructuredData, toIso } from "../../seo/jsonld.ts";
|
|
24
34
|
import { normalizeXHandle } from "../../seo/x-handle.ts";
|
|
25
35
|
import { withBase } from "../islands/base-path.ts";
|
|
36
|
+
import { isExternalUrl } from "../../core/base-path.ts";
|
|
26
37
|
import Analytics from "./Analytics.astro";
|
|
27
38
|
import WebMcp from "./WebMcp.astro";
|
|
28
39
|
import Banner from "./Banner.astro";
|
|
@@ -33,6 +44,7 @@ import Fonts from "./Fonts.astro";
|
|
|
33
44
|
import {
|
|
34
45
|
BANNER_INIT_SCRIPT,
|
|
35
46
|
SIDEBAR_SCROLL_INIT_SCRIPT,
|
|
47
|
+
SWAP_STYLESHEET_INIT_SCRIPT,
|
|
36
48
|
THEME_INIT_SCRIPT,
|
|
37
49
|
} from "./head-scripts.ts";
|
|
38
50
|
import Header from "./Header.astro";
|
|
@@ -294,6 +306,9 @@ const tocHeadings =
|
|
|
294
306
|
toc.enabled && showToc
|
|
295
307
|
? headings.filter((h) => h.depth >= toc.minLevel && h.depth <= toc.maxLevel)
|
|
296
308
|
: [];
|
|
309
|
+
// One predicate for both the outline and the page-actions divider under it,
|
|
310
|
+
// so the rail cannot draw a rule under contents that were never rendered.
|
|
311
|
+
const hasToc = tocHeadings.length > 0;
|
|
297
312
|
let gridClass =
|
|
298
313
|
"lg:grid-cols-[17.5rem_minmax(0,1fr)] xl:grid-cols-[17.5rem_minmax(0,1fr)_17.5rem]";
|
|
299
314
|
if (isBare) {
|
|
@@ -386,6 +401,7 @@ const structuredData =
|
|
|
386
401
|
base: import.meta.env.BASE_URL,
|
|
387
402
|
breadcrumbs: crumbs,
|
|
388
403
|
description,
|
|
404
|
+
identity: data.config.identity,
|
|
389
405
|
locale,
|
|
390
406
|
modified: lastModified,
|
|
391
407
|
pageType,
|
|
@@ -414,6 +430,9 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
414
430
|
supported, with Astro's simulated fade elsewhere; pairs with the
|
|
415
431
|
prefetch option in the generated Astro config. */}
|
|
416
432
|
<ClientRouter />
|
|
433
|
+
{/* Loads a next page's body-hoisted CSS before the router swaps it in —
|
|
434
|
+
see SWAP_STYLESHEET_INIT_SCRIPT for the unstyled-frame failure mode. */}
|
|
435
|
+
<script is:inline set:html={SWAP_STYLESHEET_INIT_SCRIPT} />
|
|
417
436
|
<title>{pageTitle}</title>
|
|
418
437
|
<Favicon favicon={favicon} appleIcon={appleIcon} />
|
|
419
438
|
<Fonts cssVars={fontCssVars ?? []} />
|
|
@@ -520,6 +539,7 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
520
539
|
</head>
|
|
521
540
|
<body
|
|
522
541
|
class="bg-background font-sans text-foreground antialiased"
|
|
542
|
+
data-blume-code-copy=""
|
|
523
543
|
data-blume-code-wrap={codeWrap ? "" : undefined}
|
|
524
544
|
data-blume-image-zoom={imageZoom ? "" : undefined}
|
|
525
545
|
data-i18n-copied={actionStrings.copied}
|
|
@@ -575,7 +595,7 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
575
595
|
>
|
|
576
596
|
<ul class="m-0 list-none p-0">
|
|
577
597
|
{navigation.featured.map((link) => {
|
|
578
|
-
const external =
|
|
598
|
+
const external = isExternalUrl(link.href);
|
|
579
599
|
return (
|
|
580
600
|
<li>
|
|
581
601
|
<a
|
|
@@ -676,11 +696,15 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
676
696
|
strings={navStrings}
|
|
677
697
|
wide={isApiOperation}
|
|
678
698
|
/>
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
699
|
+
{
|
|
700
|
+
hasToc && (
|
|
701
|
+
<TableOfContentsSlot
|
|
702
|
+
headings={tocHeadings}
|
|
703
|
+
title={strings.toc.title}
|
|
704
|
+
variant="mobile"
|
|
705
|
+
/>
|
|
706
|
+
)
|
|
707
|
+
}
|
|
684
708
|
<PageHeaderSlot headings={tocHeadings} page={page} route={page.route} />
|
|
685
709
|
<article class:list={[articleClass]} dir={contentDir}>
|
|
686
710
|
<slot />
|
|
@@ -711,12 +735,15 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
711
735
|
class="sticky top-16 hidden h-[calc(100dvh-4rem)] scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent overflow-y-auto px-4 pt-6 pb-10 text-sm xl:block"
|
|
712
736
|
data-blume-toc
|
|
713
737
|
>
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
738
|
+
{hasToc && (
|
|
739
|
+
<TableOfContentsSlot
|
|
740
|
+
headings={tocHeadings}
|
|
741
|
+
title={strings.toc.title}
|
|
742
|
+
variant="desktop"
|
|
743
|
+
/>
|
|
744
|
+
)}
|
|
719
745
|
<PageActions
|
|
746
|
+
divider={hasToc}
|
|
720
747
|
editUrl={editUrl}
|
|
721
748
|
exportEpub={exportEpub}
|
|
722
749
|
exportPdf={exportPdf}
|
|
@@ -906,7 +933,9 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
906
933
|
if (zoomTargets.length > 0) {
|
|
907
934
|
// Lazy: pages without a zoomable image never load the library,
|
|
908
935
|
// matching how mermaid is only fetched on pages with a diagram.
|
|
909
|
-
const { default: mediumZoom } = await import(
|
|
936
|
+
const { default: mediumZoom } = await import(
|
|
937
|
+
"medium-zoom/dist/pure"
|
|
938
|
+
);
|
|
910
939
|
mediumZoom(zoomTargets, {
|
|
911
940
|
background:
|
|
912
941
|
"color-mix(in oklab, var(--color-background) 80%, transparent)",
|
|
@@ -923,7 +952,9 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
923
952
|
</script>
|
|
924
953
|
<style is:global>
|
|
925
954
|
/* medium-zoom ships no z-index; lift the lightbox above the chrome
|
|
926
|
-
(header/sidebar) the way the previous z-[100] overlay sat.
|
|
955
|
+
(header/sidebar) the way the previous z-[100] overlay sat. Its own
|
|
956
|
+
rules come from the `medium-zoom/dist/style.css` import in the
|
|
957
|
+
frontmatter, not runtime injection. */
|
|
927
958
|
.medium-zoom-overlay {
|
|
928
959
|
backdrop-filter: blur(4px);
|
|
929
960
|
z-index: 100;
|
|
@@ -932,8 +963,8 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
932
963
|
z-index: 101;
|
|
933
964
|
}
|
|
934
965
|
@media (prefers-reduced-motion: reduce) {
|
|
935
|
-
/* html prefix outranks the library's
|
|
936
|
-
|
|
966
|
+
/* html prefix outranks the library's rules regardless of sheet
|
|
967
|
+
order; its transition declarations carry !important. */
|
|
937
968
|
html .medium-zoom-image,
|
|
938
969
|
html .medium-zoom-overlay {
|
|
939
970
|
transition: none !important;
|