blume 2.0.3 → 2.1.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/AGENTS.md +1 -0
- package/CHANGELOG.md +142 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-5f86nr5m.js → chunk-073rdvgb.js} +21 -48
- package/dist/cli/{chunk-5f86nr5m.js.map → chunk-073rdvgb.js.map} +4 -5
- package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
- package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
- package/dist/cli/chunk-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-9t7a85s3.js → chunk-313vnn40.js} +30 -19
- package/dist/cli/chunk-313vnn40.js.map +10 -0
- package/dist/cli/{chunk-tkacnehg.js → chunk-3v3ffbrj.js} +2 -2
- package/dist/cli/{chunk-q58y5e6a.js → chunk-3x6x3pph.js} +46 -24
- package/dist/cli/chunk-3x6x3pph.js.map +10 -0
- package/dist/cli/{chunk-1d7ve1dm.js → chunk-42t4jxdx.js} +37 -5
- package/dist/cli/chunk-42t4jxdx.js.map +12 -0
- package/dist/cli/{chunk-8ktnccpt.js → chunk-54ztw1nj.js} +21 -2
- package/dist/cli/chunk-54ztw1nj.js.map +10 -0
- package/dist/cli/{chunk-hm3vjy5s.js → chunk-5vjtgs6e.js} +28 -10
- package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-5vjtgs6e.js.map} +4 -4
- package/dist/cli/chunk-66w9j7p6.js +322 -0
- package/dist/cli/chunk-66w9j7p6.js.map +14 -0
- package/dist/cli/{chunk-2hsdwb9n.js → chunk-7ppf1rva.js} +34 -18
- package/dist/cli/chunk-7ppf1rva.js.map +10 -0
- package/dist/cli/{chunk-r9rcc4w7.js → chunk-8y2kmjv8.js} +7 -7
- package/dist/cli/{chunk-r20tn01b.js → chunk-c1y538kd.js} +8 -2
- package/dist/cli/{chunk-r20tn01b.js.map → chunk-c1y538kd.js.map} +3 -3
- package/dist/cli/{chunk-2eytanqx.js → chunk-c887fj0s.js} +2 -2
- package/dist/cli/{chunk-p3v96n38.js → chunk-cfyg47zg.js} +16 -6
- package/dist/cli/chunk-cfyg47zg.js.map +10 -0
- package/dist/cli/chunk-cm89scdv.js +443 -0
- package/dist/cli/chunk-cm89scdv.js.map +11 -0
- package/dist/cli/{chunk-akbpwfxc.js → chunk-dzwehpy7.js} +2 -2
- package/dist/cli/{chunk-wrr3j9w9.js → chunk-e6atmn4h.js} +299 -72
- package/dist/cli/chunk-e6atmn4h.js.map +36 -0
- package/dist/cli/{chunk-tzmab476.js → chunk-e8xw9qkh.js} +8 -6
- package/dist/cli/{chunk-tzmab476.js.map → chunk-e8xw9qkh.js.map} +3 -3
- package/dist/cli/{chunk-6dsbexzp.js → chunk-e9q7a2ak.js} +17 -15
- package/dist/cli/{chunk-6dsbexzp.js.map → chunk-e9q7a2ak.js.map} +2 -2
- package/dist/cli/{chunk-pv29h0wf.js → chunk-ehweg4fr.js} +5155 -1249
- package/dist/cli/chunk-ehweg4fr.js.map +218 -0
- package/dist/cli/{chunk-c8chx29p.js → chunk-ep6nd0rn.js} +390 -318
- package/dist/cli/chunk-ep6nd0rn.js.map +36 -0
- package/dist/cli/{chunk-p73c0m7w.js → chunk-fdrkhpjy.js} +18 -15
- package/dist/cli/{chunk-p73c0m7w.js.map → chunk-fdrkhpjy.js.map} +3 -3
- package/dist/cli/{chunk-2z928egk.js → chunk-ft9vfys6.js} +5 -5
- package/dist/cli/{chunk-5m5nmvyq.js → chunk-gb420vvz.js} +316 -79
- package/dist/cli/chunk-gb420vvz.js.map +17 -0
- package/dist/cli/{chunk-vg9r4eb9.js → chunk-hebn0tpx.js} +86 -13
- package/dist/cli/chunk-hebn0tpx.js.map +14 -0
- package/dist/cli/{chunk-35d4wj9f.js → chunk-htrwgabr.js} +41 -23
- package/dist/cli/chunk-htrwgabr.js.map +15 -0
- package/dist/cli/{chunk-g4hq16wv.js → chunk-hx3xqnk9.js} +16 -14
- package/dist/cli/{chunk-g4hq16wv.js.map → chunk-hx3xqnk9.js.map} +2 -2
- package/dist/cli/chunk-j134vrp2.js +65 -0
- package/dist/cli/chunk-j134vrp2.js.map +10 -0
- package/dist/cli/{chunk-a58773jm.js → chunk-kma9f6sf.js} +2 -2
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-ey84smr6.js → chunk-mrcccgnh.js} +6 -6
- package/dist/cli/{chunk-ey84smr6.js.map → chunk-mrcccgnh.js.map} +1 -1
- package/dist/cli/{chunk-yfyb25rh.js → chunk-n0v7m8ry.js} +24 -8
- package/dist/cli/chunk-n0v7m8ry.js.map +14 -0
- package/dist/cli/{chunk-j85vccga.js → chunk-ncj7w596.js} +576 -367
- package/dist/cli/chunk-ncj7w596.js.map +35 -0
- package/dist/cli/{chunk-5xvm6tfj.js → chunk-nq1d20w2.js} +17 -15
- package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-nq1d20w2.js.map} +2 -2
- package/dist/cli/{chunk-pehfxfta.js → chunk-nrns10mj.js} +6 -98
- package/dist/cli/chunk-nrns10mj.js.map +10 -0
- package/dist/cli/{chunk-crgn1q09.js → chunk-sds923fa.js} +4 -4
- package/dist/cli/chunk-t2m5j4pr.js +104 -0
- package/dist/cli/chunk-t2m5j4pr.js.map +11 -0
- package/dist/cli/{chunk-acanzt5p.js → chunk-tdqhyxfz.js} +6 -6
- package/dist/cli/chunk-tphac74f.js +126 -0
- package/dist/cli/chunk-tphac74f.js.map +11 -0
- package/dist/cli/chunk-w1r4vase.js +18 -0
- package/dist/cli/chunk-w1r4vase.js.map +10 -0
- package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
- package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
- package/dist/cli/index.js +31 -20
- package/dist/cli/index.js.map +5 -5
- package/dist/types/ai/agent-surface.d.ts +8 -7
- package/dist/types/ai/ai-catalog.d.ts +7 -0
- package/dist/types/ai/ask-context.d.ts +26 -0
- package/dist/types/ai/ask.d.ts +271 -58
- package/dist/types/ai/component-markdown.d.ts +2 -0
- package/dist/types/ai/index.d.ts +2 -2
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/ai/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +16 -0
- package/dist/types/astro/integration.d.ts +9 -0
- package/dist/types/astro/markdown-negotiation.d.ts +3 -0
- package/dist/types/astro/pages.d.ts +10 -0
- package/dist/types/captcha/hcaptcha.d.ts +27 -0
- package/dist/types/captcha/index.d.ts +28 -0
- package/dist/types/captcha/schema.d.ts +29 -0
- package/dist/types/captcha/turnstile.d.ts +28 -0
- package/dist/types/cli/init/scaffold.d.ts +1 -1
- package/dist/types/components/content/api-field.d.ts +62 -0
- package/dist/types/components/content/api-page.d.ts +100 -0
- package/dist/types/components/layout/nav-utils.d.ts +26 -0
- package/dist/types/components/openapi/code-samples.d.ts +29 -0
- package/dist/types/components/openapi/helpers.d.ts +158 -0
- package/dist/types/components/openapi/security.d.ts +95 -0
- package/dist/types/components/openapi/snippets.d.ts +60 -0
- package/dist/types/consent/ethyca.d.ts +48 -0
- package/dist/types/consent/index.d.ts +37 -0
- package/dist/types/consent/native.d.ts +28 -0
- package/dist/types/consent/osano.d.ts +42 -0
- package/dist/types/consent/schema.d.ts +110 -0
- package/dist/types/core/changelog-index.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +217 -23
- package/dist/types/core/data.d.ts +53 -4
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/i18n-ui.d.ts +88 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/links.d.ts +8 -0
- package/dist/types/core/localizable.d.ts +7 -0
- package/dist/types/core/page-modes.d.ts +29 -0
- package/dist/types/core/probe.d.ts +2 -0
- package/dist/types/core/project-graph.d.ts +6 -0
- package/dist/types/core/redirect-patterns.d.ts +152 -0
- package/dist/types/core/related.d.ts +40 -0
- package/dist/types/core/schema.d.ts +670 -56
- package/dist/types/core/sources/cache.d.ts +3 -0
- package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
- package/dist/types/core/sources/contentful.d.ts +17 -1
- package/dist/types/core/sources/lexical.d.ts +5 -0
- package/dist/types/core/sources/normalize.d.ts +20 -1
- package/dist/types/core/sources/read.d.ts +5 -0
- package/dist/types/core/sources/remote.d.ts +37 -10
- package/dist/types/core/sources/sanity.d.ts +1 -1
- package/dist/types/core/types.d.ts +18 -1
- package/dist/types/core/variables.d.ts +42 -0
- package/dist/types/deploy/adapter-output.d.ts +2 -1
- package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
- package/dist/types/deploy/adapters/node.d.ts +58 -23
- package/dist/types/deploy/adapters/registry.d.ts +10 -0
- package/dist/types/deploy/adapters/types.d.ts +3 -2
- package/dist/types/deploy/node-headers.d.ts +13 -2
- package/dist/types/deploy/platforms/cloudflare.d.ts +15 -7
- package/dist/types/deploy/platforms/netlify.d.ts +7 -0
- package/dist/types/deploy/platforms/node.d.ts +3 -1
- package/dist/types/deploy/platforms/types.d.ts +18 -7
- package/dist/types/deploy/platforms/vercel.d.ts +26 -1
- package/dist/types/deploy/redirects.d.ts +55 -11
- package/dist/types/deploy/sitemap.d.ts +2 -1
- package/dist/types/deploy/vercel-negotiation.d.ts +51 -21
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/features.d.ts +3 -2
- package/dist/types/narration/build.d.ts +70 -0
- package/dist/types/narration/provider.d.ts +26 -0
- package/dist/types/narration/script.d.ts +154 -0
- package/dist/types/openapi/checks.d.ts +27 -0
- package/dist/types/openapi/graphql.d.ts +6 -0
- package/dist/types/openapi/model.d.ts +27 -10
- package/dist/types/openapi/overlay.d.ts +54 -0
- package/dist/types/openapi/parse.d.ts +16 -0
- package/dist/types/openapi/references.d.ts +30 -4
- package/dist/types/ratelimit/cloudflare.d.ts +39 -0
- package/dist/types/ratelimit/index.d.ts +27 -0
- package/dist/types/ratelimit/memory.d.ts +36 -0
- package/dist/types/ratelimit/schema.d.ts +119 -0
- package/dist/types/ratelimit/upstash.d.ts +46 -0
- package/dist/types/ratelimit/wrangler.d.ts +23 -0
- package/dist/types/reference/asyncapi.d.ts +10 -10
- package/dist/types/reference/graphql.d.ts +89 -11
- package/dist/types/reference/index.d.ts +1 -1
- package/dist/types/reference/openapi.d.ts +33 -12
- package/dist/types/reference/options.d.ts +82 -6
- package/dist/types/reference/scalar.d.ts +13 -0
- package/dist/types/reference/schema.d.ts +144 -36
- package/dist/types/search/adapters/registry.d.ts +3 -0
- package/dist/types/search/adapters/typesense.d.ts +14 -4
- package/dist/types/search/build.d.ts +3 -1
- package/dist/types/search/documents.d.ts +12 -1
- package/dist/types/search/orama-index.d.ts +13 -9
- package/dist/types/search/sync/algolia.d.ts +5 -2
- package/dist/types/search/sync/index.d.ts +3 -1
- package/dist/types/search/sync/typesense.d.ts +11 -7
- package/dist/types/seo/metatags.d.ts +20 -0
- package/dist/types/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +54 -0
- package/dist/types/sources/shared.d.ts +3 -0
- package/dist/types/sources/strapi.d.ts +2 -0
- package/docs/01-quickstart.mdx +4 -2
- package/docs/02-deployment.mdx +71 -17
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/advanced/changelog.mdx +13 -2
- package/docs/advanced/skills.mdx +15 -1
- package/docs/cli/audit.mdx +51 -8
- package/docs/cli/doctor.mdx +5 -1
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/index.mdx +1 -0
- package/docs/cli/translate.mdx +1 -0
- package/docs/cli/validate.mdx +4 -3
- package/docs/configuration/analytics.mdx +3 -1
- package/docs/configuration/assistant.mdx +194 -38
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +8 -6
- package/docs/configuration/export.mdx +5 -3
- package/docs/configuration/index.mdx +64 -5
- package/docs/configuration/meta.ts +3 -0
- package/docs/configuration/narration.mdx +119 -0
- package/docs/configuration/rate-limiting.mdx +90 -0
- package/docs/configuration/search.mdx +63 -12
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +108 -2
- package/docs/content/frontmatter.mdx +80 -2
- package/docs/content/i18n.mdx +39 -4
- package/docs/content/includes.mdx +16 -0
- package/docs/content/meta.mdx +1 -0
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +32 -10
- package/docs/content/sources/contentful.mdx +35 -0
- package/docs/content/sources/custom.mdx +43 -0
- package/docs/content/sources/github-releases.mdx +39 -0
- package/docs/content/sources/index.mdx +91 -0
- package/docs/content/sources/meta.ts +16 -0
- package/docs/content/sources/notion.mdx +32 -0
- package/docs/content/sources/obsidian.mdx +37 -0
- package/docs/content/sources/payload.mdx +31 -0
- package/docs/content/sources/remote-mdx.mdx +44 -0
- package/docs/content/sources/sanity.mdx +29 -0
- package/docs/content/sources/strapi.mdx +32 -0
- package/docs/content/syntax.mdx +69 -4
- package/docs/content/variables.mdx +41 -0
- package/docs/discoverability/agent-discovery.mdx +29 -2
- package/docs/discoverability/index.mdx +2 -2
- package/docs/discoverability/json-api.mdx +2 -0
- package/docs/discoverability/llms-txt.mdx +2 -2
- package/docs/discoverability/markdown.mdx +3 -1
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +4 -2
- package/docs/references/graphql.mdx +14 -2
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +148 -7
- package/docs/references/scalar.mdx +3 -1
- package/package.json +43 -8
- package/skills/blume/SKILL.md +3 -0
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +7 -7
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- package/skills/blume-migrate/references/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +10 -7
- package/skills/blume-migrate/references/starlight.md +11 -8
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +6 -3
- package/src/ai/ask-context.ts +103 -20
- package/src/ai/ask-data.ts +6 -0
- package/src/ai/ask-limits.ts +13 -0
- package/src/ai/ask-tools.ts +142 -0
- package/src/ai/ask.ts +354 -76
- package/src/ai/changelog-markdown.ts +11 -6
- package/src/ai/component-markdown.ts +76 -1
- package/src/ai/cors.ts +14 -3
- package/src/ai/index.ts +12 -0
- package/src/ai/llms.ts +6 -2
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/mcp/query.ts +14 -6
- package/src/ai/openapi-components.ts +36 -6
- package/src/ai/relative-links.ts +4 -38
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +35 -2
- package/src/ai/write-skill.ts +97 -0
- package/src/astro/generate.ts +210 -66
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +42 -4
- package/src/astro/markdown-negotiation.ts +3 -0
- package/src/astro/module-types.ts +9 -1
- package/src/astro/pages.ts +23 -0
- package/src/astro/templates.ts +406 -108
- package/src/astro/variables.ts +31 -0
- package/src/audit/catalog.ts +38 -3
- package/src/audit/checks/contrast.ts +183 -0
- package/src/audit/checks/i18n.ts +2 -3
- package/src/audit/checks/indexability.ts +2 -3
- package/src/audit/checks/links.ts +19 -13
- package/src/audit/checks/network.ts +110 -6
- package/src/audit/checks/redirects.ts +6 -4
- package/src/audit/checks/robots.ts +58 -2
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +5 -0
- package/src/audit/types.ts +4 -1
- package/src/audit/url.ts +14 -5
- package/src/blume-modules.d.ts +12 -1
- package/src/captcha/client.ts +152 -0
- package/src/captcha/hcaptcha.ts +36 -0
- package/src/captcha/index.ts +28 -0
- package/src/captcha/schema.ts +32 -0
- package/src/captcha/turnstile.ts +38 -0
- package/src/captcha/verify.ts +71 -0
- package/src/cli/args.ts +51 -0
- package/src/cli/build-failure.ts +23 -0
- package/src/cli/command-meta.ts +5 -0
- package/src/cli/commands/audit.ts +10 -1
- package/src/cli/commands/dev.ts +18 -2
- package/src/cli/commands/doctor.ts +67 -28
- package/src/cli/commands/eval.ts +36 -4
- package/src/cli/commands/init.ts +6 -4
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/commands/validate.ts +10 -1
- package/src/cli/index.ts +5 -0
- package/src/cli/init/scaffold.ts +3 -3
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +72 -30
- package/src/components/BlumePage.astro +2 -0
- package/src/components/content/ApiEndpoint.astro +18 -0
- package/src/components/content/ApiField.astro +79 -0
- package/src/components/content/ApiPlayground.astro +66 -0
- package/src/components/content/ApiRail.astro +19 -0
- package/src/components/content/Color.astro +1 -0
- package/src/components/content/FileTree.astro +1 -0
- package/src/components/content/GithubInfo.astro +1 -0
- package/src/components/content/Panel.astro +1 -0
- package/src/components/content/ParamField.astro +25 -0
- package/src/components/content/RequestExample.astro +17 -0
- package/src/components/content/ResponseExample.astro +17 -0
- package/src/components/content/ResponseField.astro +32 -0
- package/src/components/content/Step.astro +1 -0
- package/src/components/content/Tile.astro +1 -0
- package/src/components/content/Tooltip.astro +1 -0
- package/src/components/content/Tree.astro +1 -0
- package/src/components/content/TypeTable.astro +4 -1
- package/src/components/content/View.astro +21 -0
- package/src/components/content/ViewSwitcher.astro +168 -0
- package/src/components/content/api-field.ts +103 -0
- package/src/components/content/api-page.ts +248 -0
- package/src/components/content/mermaid-element.ts +15 -1
- package/src/components/content/view-switcher.ts +71 -0
- package/src/components/index.ts +2 -0
- package/src/components/islands/Assistant.astro +10 -1
- package/src/components/islands/assistant.tsx +104 -14
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +136 -21
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +6 -2
- package/src/components/layout/ConsentBanner.astro +62 -0
- package/src/components/layout/ConsentHead.astro +38 -0
- package/src/components/layout/Directory.astro +94 -0
- package/src/components/layout/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +122 -80
- package/src/components/layout/LanguageSwitcher.astro +48 -15
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +33 -7
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +71 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +318 -47
- package/src/components/layout/Search.astro +47 -19
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -0
- package/src/components/layout/TableOfContents.astro +24 -17
- package/src/components/layout/consent/native.ts +79 -0
- package/src/components/layout/consent/types.ts +37 -0
- package/src/components/layout/footer-links.ts +53 -0
- package/src/components/layout/footer-socials.ts +87 -0
- package/src/components/layout/head-scripts.ts +6 -1
- package/src/components/layout/locale-redirect.ts +107 -0
- package/src/components/layout/nav-utils.ts +83 -0
- package/src/components/layout/page-feedback.ts +96 -0
- package/src/components/layout/search/algolia.ts +10 -1
- package/src/components/layout/search/analytics.ts +126 -0
- package/src/components/layout/search/endpoint.ts +48 -5
- package/src/components/layout/search/flexsearch.ts +17 -5
- package/src/components/layout/search/orama-cloud.ts +26 -14
- package/src/components/layout/search/pagefind.ts +114 -5
- package/src/components/layout/search/types.ts +32 -0
- package/src/components/layout/search/typesense.ts +19 -3
- package/src/components/openapi/AsyncApiOperation.astro +14 -7
- package/src/components/openapi/Authorization.astro +10 -0
- package/src/components/openapi/GraphqlOperation.astro +5 -0
- package/src/components/openapi/Operation.astro +184 -13
- package/src/components/openapi/ParametersTable.astro +8 -4
- package/src/components/openapi/Playground.astro +7 -1
- package/src/components/openapi/RequestBody.astro +24 -5
- package/src/components/openapi/RequestPanel.astro +59 -7
- package/src/components/openapi/Responses.astro +7 -4
- package/src/components/openapi/SchemaTable.astro +15 -3
- package/src/components/openapi/async-snippets.ts +65 -9
- package/src/components/openapi/async.ts +123 -29
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/graphql-helpers.ts +35 -12
- package/src/components/openapi/helpers.ts +36 -2
- package/src/components/openapi/message-model.ts +54 -11
- package/src/components/openapi/message.ts +14 -4
- package/src/components/openapi/operation-model.ts +16 -7
- package/src/components/openapi/playground-client.ts +24 -5
- package/src/components/openapi/playground-schema.ts +1 -1
- package/src/components/openapi/schema-tree.ts +44 -14
- package/src/components/openapi/security.ts +16 -3
- package/src/components/openapi/snippets.ts +873 -10
- package/src/components/openapi/validate-json.ts +4 -3
- package/src/components/props.ts +6 -0
- package/src/consent/client.ts +95 -0
- package/src/consent/clients.ts +19 -0
- package/src/consent/ethyca.ts +71 -0
- package/src/consent/head.ts +24 -0
- package/src/consent/index.ts +37 -0
- package/src/consent/init.ts +29 -0
- package/src/consent/native.ts +36 -0
- package/src/consent/osano.ts +59 -0
- package/src/consent/schema.ts +30 -0
- package/src/core/builtin-tags.ts +5 -0
- package/src/core/changelog-index.ts +31 -1
- package/src/core/client-address.ts +19 -0
- package/src/core/component-diagnostics.ts +53 -0
- package/src/core/component-overrides.ts +32 -0
- package/src/core/config-input.ts +225 -23
- package/src/core/data.ts +46 -4
- package/src/core/diagnostics.ts +8 -2
- package/src/core/footer.ts +26 -0
- package/src/core/graph.ts +39 -36
- package/src/core/i18n-ui.ts +84 -2
- package/src/core/includes.ts +21 -4
- package/src/core/links.ts +63 -19
- package/src/core/localizable.ts +26 -0
- package/src/core/navigation.ts +43 -12
- package/src/core/page-modes.ts +56 -0
- package/src/core/probe.ts +26 -6
- package/src/core/project-graph.ts +163 -16
- package/src/core/redirect-patterns.ts +520 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +339 -66
- package/src/core/sources/cache.ts +6 -0
- package/src/core/sources/contentful-rich-text.ts +29 -6
- package/src/core/sources/contentful.ts +135 -25
- package/src/core/sources/filesystem.ts +26 -4
- package/src/core/sources/github-releases.ts +12 -1
- package/src/core/sources/lexical.ts +15 -10
- package/src/core/sources/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +228 -41
- package/src/core/sources/notion.ts +7 -1
- package/src/core/sources/obsidian.ts +16 -3
- package/src/core/sources/payload.ts +38 -15
- package/src/core/sources/read.ts +8 -3
- package/src/core/sources/remote.ts +72 -15
- package/src/core/sources/resolve.ts +11 -1
- package/src/core/sources/sanity.ts +43 -25
- package/src/core/sources/strapi.ts +2 -2
- package/src/core/types.ts +19 -1
- package/src/core/ui-packs/ar.ts +44 -0
- package/src/core/ui-packs/bg.ts +46 -0
- package/src/core/ui-packs/bn.ts +44 -0
- package/src/core/ui-packs/ca.ts +47 -0
- package/src/core/ui-packs/cs.ts +45 -0
- package/src/core/ui-packs/da.ts +46 -0
- package/src/core/ui-packs/de.ts +47 -0
- package/src/core/ui-packs/el.ts +46 -0
- package/src/core/ui-packs/es.ts +47 -0
- package/src/core/ui-packs/fa.ts +46 -0
- package/src/core/ui-packs/fi.ts +46 -0
- package/src/core/ui-packs/fr.ts +47 -0
- package/src/core/ui-packs/he.ts +44 -0
- package/src/core/ui-packs/hi.ts +45 -0
- package/src/core/ui-packs/hr.ts +46 -0
- package/src/core/ui-packs/hu.ts +45 -0
- package/src/core/ui-packs/id.ts +46 -0
- package/src/core/ui-packs/it.ts +46 -0
- package/src/core/ui-packs/ja.ts +46 -0
- package/src/core/ui-packs/ko.ts +45 -0
- package/src/core/ui-packs/nl.ts +47 -0
- package/src/core/ui-packs/no.ts +45 -0
- package/src/core/ui-packs/pl.ts +46 -0
- package/src/core/ui-packs/pt-br.ts +47 -0
- package/src/core/ui-packs/pt.ts +46 -0
- package/src/core/ui-packs/ro.ts +46 -0
- package/src/core/ui-packs/ru.ts +46 -0
- package/src/core/ui-packs/sk.ts +45 -0
- package/src/core/ui-packs/sr.ts +46 -0
- package/src/core/ui-packs/sv.ts +45 -0
- package/src/core/ui-packs/th.ts +44 -0
- package/src/core/ui-packs/tr.ts +46 -0
- package/src/core/ui-packs/uk.ts +46 -0
- package/src/core/ui-packs/vi.ts +46 -0
- package/src/core/ui-packs/zh-tw.ts +44 -0
- package/src/core/ui-packs/zh.ts +44 -0
- package/src/core/variables.ts +103 -0
- package/src/deploy/adapter-output.ts +7 -2
- package/src/deploy/adapters/cloudflare.ts +7 -5
- package/src/deploy/adapters/node.ts +47 -9
- package/src/deploy/adapters/types.ts +7 -3
- package/src/deploy/artifacts.ts +79 -9
- package/src/deploy/cloudflare-negotiation.ts +35 -5
- package/src/deploy/headers.ts +2 -1
- package/src/deploy/node-headers.ts +63 -9
- package/src/deploy/platforms/cloudflare.ts +45 -9
- package/src/deploy/platforms/netlify.ts +35 -2
- package/src/deploy/platforms/node.ts +12 -2
- package/src/deploy/platforms/types.ts +21 -7
- package/src/deploy/platforms/vercel.ts +149 -17
- package/src/deploy/redirects.ts +151 -24
- package/src/deploy/rss.ts +3 -1
- package/src/deploy/sitemap.ts +39 -5
- package/src/deploy/vercel-negotiation.ts +326 -102
- package/src/eval/agents.ts +18 -1
- package/src/eval/prompts.ts +6 -3
- package/src/eval/report.ts +34 -13
- package/src/eval/run.ts +5 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/features.ts +3 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +28 -5
- package/src/markdown/math.ts +4 -4
- package/src/markdown/relative-links.ts +3 -2
- package/src/markdown/variables.ts +56 -0
- package/src/markdown/views.ts +82 -0
- package/src/narration/build.ts +412 -0
- package/src/narration/player.ts +249 -0
- package/src/narration/provider.ts +29 -0
- package/src/narration/script.ts +592 -0
- package/src/og/card.ts +7 -6
- package/src/openapi/checks.ts +273 -0
- package/src/openapi/graphql-build.ts +21 -0
- package/src/openapi/graphql.ts +11 -0
- package/src/openapi/model.ts +72 -27
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +182 -4
- package/src/openapi/references.ts +64 -8
- package/src/openapi/render-mdx.ts +5 -1
- package/src/openapi/scalar.ts +40 -6
- package/src/openapi/source.ts +40 -15
- package/src/ratelimit/cloudflare.ts +54 -0
- package/src/ratelimit/index.ts +30 -0
- package/src/ratelimit/memory.ts +45 -0
- package/src/ratelimit/runtime.ts +247 -0
- package/src/ratelimit/schema.ts +28 -0
- package/src/ratelimit/upstash.ts +60 -0
- package/src/ratelimit/wrangler.ts +70 -0
- package/src/reference/asyncapi.ts +2 -2
- package/src/reference/graphql.ts +20 -3
- package/src/reference/index.ts +1 -0
- package/src/reference/openapi.ts +21 -7
- package/src/reference/options.ts +80 -8
- package/src/reference/scalar.ts +33 -8
- package/src/registry/eject.ts +23 -5
- package/src/registry/registry.ts +7 -0
- package/src/search/adapters/locale-scope.ts +30 -0
- package/src/search/adapters/typesense.ts +14 -4
- package/src/search/build.ts +21 -3
- package/src/search/documents.ts +26 -2
- package/src/search/orama-index.ts +191 -34
- package/src/search/pagefind-ranking.ts +39 -0
- package/src/search/source-pages.ts +29 -0
- package/src/search/sync/algolia.ts +111 -10
- package/src/search/sync/index.ts +52 -13
- package/src/search/sync/orama-cloud.ts +3 -0
- package/src/search/sync/typesense.ts +76 -26
- package/src/seo/metatags.ts +67 -0
- package/src/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/shared.ts +3 -0
- package/src/theme/chrome-icons.ts +1 -0
- package/src/theme/color.ts +500 -0
- package/src/theme/entry.ts +85 -6
- package/src/theme/palette.ts +121 -45
- package/src/translate/prompts.ts +1 -0
- package/src/translate/validate.ts +31 -3
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-1d7ve1dm.js.map +0 -12
- package/dist/cli/chunk-2hsdwb9n.js.map +0 -10
- package/dist/cli/chunk-35d4wj9f.js.map +0 -15
- package/dist/cli/chunk-364znk6q.js +0 -75
- package/dist/cli/chunk-364znk6q.js.map +0 -11
- package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
- package/dist/cli/chunk-8ktnccpt.js.map +0 -10
- package/dist/cli/chunk-9t7a85s3.js.map +0 -10
- package/dist/cli/chunk-b07cmahc.js +0 -145
- package/dist/cli/chunk-b07cmahc.js.map +0 -11
- package/dist/cli/chunk-c8chx29p.js.map +0 -36
- package/dist/cli/chunk-e04dxsz1.js +0 -39
- package/dist/cli/chunk-e04dxsz1.js.map +0 -10
- package/dist/cli/chunk-j85vccga.js.map +0 -36
- package/dist/cli/chunk-p3v96n38.js.map +0 -10
- package/dist/cli/chunk-pehfxfta.js.map +0 -10
- package/dist/cli/chunk-pv29h0wf.js.map +0 -190
- package/dist/cli/chunk-q58y5e6a.js.map +0 -10
- package/dist/cli/chunk-vg9r4eb9.js.map +0 -14
- package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
- package/dist/cli/chunk-yfyb25rh.js.map +0 -14
- package/docs/content/sources.mdx +0 -334
- /package/dist/cli/{chunk-tkacnehg.js.map → chunk-3v3ffbrj.js.map} +0 -0
- /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-8y2kmjv8.js.map} +0 -0
- /package/dist/cli/{chunk-2eytanqx.js.map → chunk-c887fj0s.js.map} +0 -0
- /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-dzwehpy7.js.map} +0 -0
- /package/dist/cli/{chunk-2z928egk.js.map → chunk-ft9vfys6.js.map} +0 -0
- /package/dist/cli/{chunk-a58773jm.js.map → chunk-kma9f6sf.js.map} +0 -0
- /package/dist/cli/{chunk-crgn1q09.js.map → chunk-sds923fa.js.map} +0 -0
- /package/dist/cli/{chunk-acanzt5p.js.map → chunk-tdqhyxfz.js.map} +0 -0
|
@@ -19,9 +19,15 @@ interface Props {
|
|
|
19
19
|
* overflow the viewport — pass `end` there.
|
|
20
20
|
*/
|
|
21
21
|
align?: "start" | "end";
|
|
22
|
+
/**
|
|
23
|
+
* `bar` (the default) for the header; `drawer` for the top of the nav
|
|
24
|
+
* drawer, where the trigger spans the drawer, always shows its label, and
|
|
25
|
+
* opens a panel as wide as itself, so nothing reaches past the drawer edge.
|
|
26
|
+
*/
|
|
27
|
+
variant?: "bar" | "drawer";
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
const { selector, route, align = "start" } = Astro.props;
|
|
30
|
+
const { selector, route, align = "start", variant = "bar" } = Astro.props;
|
|
25
31
|
|
|
26
32
|
// The active item is the deepest path the current route sits under (on a path
|
|
27
33
|
// boundary, so `/api` never claims `/api-reference` routes), falling back to
|
|
@@ -37,25 +43,37 @@ const active =
|
|
|
37
43
|
// version like "v2" or "Latest"): on a phone a bare chevron doesn't say what
|
|
38
44
|
// it switches. Longer product names still collapse to the icon there.
|
|
39
45
|
const label = active?.label ?? selector.label;
|
|
40
|
-
|
|
46
|
+
let labelClass: string | undefined;
|
|
47
|
+
if (variant === "drawer") {
|
|
48
|
+
labelClass = "flex-1 text-start";
|
|
49
|
+
} else if (label.length > 6) {
|
|
50
|
+
labelClass = "max-sm:hidden";
|
|
51
|
+
}
|
|
41
52
|
// The accessible name carries the visible active label too (WCAG 2.5.3,
|
|
42
53
|
// label in name), so a voice-control user can say "v2" to open it — the
|
|
43
54
|
// language switcher's pattern.
|
|
44
55
|
const summaryLabel =
|
|
45
56
|
label === selector.label ? selector.label : `${selector.label}: ${label}`;
|
|
46
57
|
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
// Borderless in the header, like the icon buttons and the language switcher;
|
|
59
|
+
// bordered in the drawer, where the trigger spans it.
|
|
60
|
+
const triggerClass =
|
|
61
|
+
variant === "bar"
|
|
62
|
+
? "inline-flex h-9 cursor-pointer items-center gap-1.5 rounded-full px-3 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground group-open:bg-muted group-open:text-foreground"
|
|
63
|
+
: "inline-flex h-9 w-full cursor-pointer items-center gap-1.5 rounded-full border border-border bg-background px-3 text-muted-foreground text-sm transition-colors hover:border-foreground hover:text-foreground";
|
|
49
64
|
const menuRowClass =
|
|
50
65
|
"flex w-full items-start gap-2.5 rounded-md px-2 py-1.5 text-start text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground";
|
|
51
66
|
---
|
|
52
67
|
|
|
53
68
|
{
|
|
54
69
|
selector.items.length > 0 && (
|
|
55
|
-
<details
|
|
70
|
+
<details
|
|
71
|
+
class:list={["group relative", variant === "drawer" && "w-full"]}
|
|
72
|
+
data-blume-dropdown
|
|
73
|
+
>
|
|
56
74
|
<summary
|
|
57
75
|
aria-label={summaryLabel}
|
|
58
|
-
class={
|
|
76
|
+
class:list={[triggerClass, "list-none [&::-webkit-details-marker]:hidden"]}
|
|
59
77
|
>
|
|
60
78
|
{active?.icon && <Icon name={active.icon} size={16} />}
|
|
61
79
|
<span class={labelClass}>{label}</span>
|
|
@@ -66,7 +84,15 @@ const menuRowClass =
|
|
|
66
84
|
/>
|
|
67
85
|
</summary>
|
|
68
86
|
<div
|
|
69
|
-
class={
|
|
87
|
+
class:list={[
|
|
88
|
+
"absolute z-50 mt-2 rounded-blume border border-border bg-background p-1 shadow-xl",
|
|
89
|
+
variant === "drawer"
|
|
90
|
+
? "inset-x-0"
|
|
91
|
+
: [
|
|
92
|
+
"min-w-56 max-w-[calc(100vw-1rem)]",
|
|
93
|
+
align === "end" ? "end-0" : "start-0",
|
|
94
|
+
],
|
|
95
|
+
]}
|
|
70
96
|
data-blume-dropdown-panel
|
|
71
97
|
>
|
|
72
98
|
{selector.items.map((item) => (
|
|
@@ -89,6 +89,10 @@ const providers = (openInChat ?? openInChatProviders).map((key) => ({
|
|
|
89
89
|
// keep that separator only while a configured list preserves v0 first.
|
|
90
90
|
const chatSeparator = providers.length > 1 && providers[0]?.key === "v0";
|
|
91
91
|
|
|
92
|
+
// The block renders once, for the right rail. Below `xl` the script moves it
|
|
93
|
+
// into the mobile "On this page" dropdown, and the global styles below the
|
|
94
|
+
// markup restyle it there from that container.
|
|
95
|
+
|
|
92
96
|
// Top-level actions match the ToC links: flat, no background/padding, color
|
|
93
97
|
// shift on hover only.
|
|
94
98
|
const rowClass =
|
|
@@ -98,6 +102,8 @@ const rowClass =
|
|
|
98
102
|
// background).
|
|
99
103
|
const menuRowClass =
|
|
100
104
|
"flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-start text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground";
|
|
105
|
+
const panelClass =
|
|
106
|
+
"absolute top-full right-2 z-50 mt-1 w-max min-w-[14rem] max-w-[22rem] rounded-blume border border-border bg-background p-1 shadow-xl";
|
|
101
107
|
---
|
|
102
108
|
|
|
103
109
|
<div
|
|
@@ -130,24 +136,24 @@ const menuRowClass =
|
|
|
130
136
|
<span data-blume-copy-label>{a.copyMarkdown}</span>
|
|
131
137
|
</button>
|
|
132
138
|
|
|
139
|
+
{/* Each chevron turns with its own <details> only: in the mobile dropdown
|
|
140
|
+
these nest inside the outline's <details>, and a `group-open` variant
|
|
141
|
+
would match that open ancestor too. */}
|
|
133
142
|
{
|
|
134
143
|
(exportPdf || exportEpub) && (
|
|
135
|
-
<details class="
|
|
144
|
+
<details class="relative" data-blume-dropdown>
|
|
136
145
|
<summary
|
|
137
146
|
class={`${rowClass} cursor-pointer list-none [&::-webkit-details-marker]:hidden`}
|
|
138
147
|
>
|
|
139
148
|
<Icon name="download" size={16} />
|
|
140
149
|
{a.export}
|
|
141
150
|
<Icon
|
|
142
|
-
class="ms-auto transition-transform
|
|
151
|
+
class="ms-auto transition-transform [details[open]>summary_&]:rotate-180"
|
|
143
152
|
name="chevron-down"
|
|
144
153
|
size={14}
|
|
145
154
|
/>
|
|
146
155
|
</summary>
|
|
147
|
-
<div
|
|
148
|
-
class="absolute top-full right-2 z-50 mt-1 w-max min-w-[14rem] max-w-[22rem] rounded-blume border border-border bg-background p-1 shadow-xl"
|
|
149
|
-
data-blume-menu
|
|
150
|
-
>
|
|
156
|
+
<div class={panelClass} data-blume-menu>
|
|
151
157
|
{exportPdf && (
|
|
152
158
|
<button class={menuRowClass} data-blume-export-pdf type="button">
|
|
153
159
|
<Icon name="file" size={16} />
|
|
@@ -169,22 +175,19 @@ const menuRowClass =
|
|
|
169
175
|
|
|
170
176
|
{
|
|
171
177
|
providers.length > 0 && (
|
|
172
|
-
<details class="
|
|
178
|
+
<details class="relative" data-blume-dropdown>
|
|
173
179
|
<summary
|
|
174
180
|
class={`${rowClass} cursor-pointer list-none [&::-webkit-details-marker]:hidden`}
|
|
175
181
|
>
|
|
176
182
|
<Icon name="external-link" size={16} />
|
|
177
183
|
{a.openInChat}
|
|
178
184
|
<Icon
|
|
179
|
-
class="ms-auto transition-transform
|
|
185
|
+
class="ms-auto transition-transform [details[open]>summary_&]:rotate-180"
|
|
180
186
|
name="chevron-down"
|
|
181
187
|
size={14}
|
|
182
188
|
/>
|
|
183
189
|
</summary>
|
|
184
|
-
<div
|
|
185
|
-
class="absolute top-full right-2 z-50 mt-1 w-max min-w-[14rem] max-w-[22rem] rounded-blume border border-border bg-background p-1 shadow-xl"
|
|
186
|
-
data-blume-menu
|
|
187
|
-
>
|
|
190
|
+
<div class={panelClass} data-blume-menu>
|
|
188
191
|
{providers.map((provider, index) => (
|
|
189
192
|
<>
|
|
190
193
|
{index === 1 && chatSeparator && (
|
|
@@ -208,22 +211,19 @@ const menuRowClass =
|
|
|
208
211
|
|
|
209
212
|
{
|
|
210
213
|
mcpUrl && (
|
|
211
|
-
<details class="
|
|
214
|
+
<details class="relative" data-blume-dropdown>
|
|
212
215
|
<summary
|
|
213
216
|
class={`${rowClass} cursor-pointer list-none [&::-webkit-details-marker]:hidden`}
|
|
214
217
|
>
|
|
215
218
|
<Icon name="plug" size={16} />
|
|
216
219
|
{a.connectMcp}
|
|
217
220
|
<Icon
|
|
218
|
-
class="ms-auto transition-transform
|
|
221
|
+
class="ms-auto transition-transform [details[open]>summary_&]:rotate-180"
|
|
219
222
|
name="chevron-down"
|
|
220
223
|
size={14}
|
|
221
224
|
/>
|
|
222
225
|
</summary>
|
|
223
|
-
<div
|
|
224
|
-
class="absolute top-full right-2 z-50 mt-1 w-max min-w-[14rem] max-w-[22rem] rounded-blume border border-border bg-background p-1 shadow-xl"
|
|
225
|
-
data-blume-menu
|
|
226
|
-
>
|
|
226
|
+
<div class={panelClass} data-blume-menu>
|
|
227
227
|
<button class={menuRowClass} data-mcp-copy-url type="button">
|
|
228
228
|
<Icon name="link" size={16} />
|
|
229
229
|
<span class="flex-1" data-mcp-url-label>
|
|
@@ -259,6 +259,45 @@ const menuRowClass =
|
|
|
259
259
|
}
|
|
260
260
|
</div>
|
|
261
261
|
|
|
262
|
+
<style is:global>
|
|
263
|
+
/* Below `xl` the script moves the block into the mobile "On this page"
|
|
264
|
+
dropdown (see `placeActions`), where it reads like the outline above it:
|
|
265
|
+
under a rule, its rows padded like the outline's links, its groups
|
|
266
|
+
expanding in place under their row instead of floating, and without
|
|
267
|
+
Scroll to top, since that dropdown already sits at the top of the page. */
|
|
268
|
+
[data-blume-page-actions-mount] > [data-blume-page-actions] {
|
|
269
|
+
margin-top: 0;
|
|
270
|
+
border-top: 1px solid var(--blume-border);
|
|
271
|
+
padding: 0.5rem;
|
|
272
|
+
}
|
|
273
|
+
[data-blume-page-actions-mount] [data-blume-scroll-top] {
|
|
274
|
+
display: none;
|
|
275
|
+
}
|
|
276
|
+
[data-blume-page-actions-mount]
|
|
277
|
+
:is([data-blume-page-actions] > :is(a, button), summary) {
|
|
278
|
+
border-radius: 0.375rem;
|
|
279
|
+
padding-inline: 0.5rem;
|
|
280
|
+
}
|
|
281
|
+
[data-blume-page-actions-mount] [data-blume-menu] {
|
|
282
|
+
position: static;
|
|
283
|
+
width: auto;
|
|
284
|
+
min-width: 0;
|
|
285
|
+
max-width: none;
|
|
286
|
+
margin: 0;
|
|
287
|
+
margin-inline-start: 0.5rem;
|
|
288
|
+
border-width: 0;
|
|
289
|
+
border-inline-start-width: 1px;
|
|
290
|
+
border-radius: 0;
|
|
291
|
+
background-color: transparent;
|
|
292
|
+
padding: 0;
|
|
293
|
+
padding-inline-start: 0.5rem;
|
|
294
|
+
box-shadow: none;
|
|
295
|
+
}
|
|
296
|
+
[data-blume-page-actions-mount] [data-blume-menu] > :hover {
|
|
297
|
+
background-color: transparent;
|
|
298
|
+
}
|
|
299
|
+
</style>
|
|
300
|
+
|
|
262
301
|
<script>
|
|
263
302
|
import { copyDeferredText, copyText, flashLabel } from "../copy-feedback.ts";
|
|
264
303
|
import { installDropdownDismiss } from "../dropdown-dismiss.ts";
|
|
@@ -301,13 +340,42 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
301
340
|
// for the EPUB: drop decorative SVGs and interactive controls (which render as
|
|
302
341
|
// broken boxes in a reader), flatten Shiki's variable-colored code to plain
|
|
303
342
|
// monospace text, and reveal panels the tab scripts would otherwise hide.
|
|
304
|
-
|
|
343
|
+
// Links point at the site: a site-relative `/docs/page` means nothing inside
|
|
344
|
+
// the book, so every link but an in-page `#fragment` is made absolute.
|
|
345
|
+
const buildEpubChapterHtml = async (article: Element): Promise<string> => {
|
|
305
346
|
const clone = article.cloneNode(true) as Element;
|
|
306
347
|
for (const el of clone.querySelectorAll(
|
|
307
348
|
"svg, button, select, script, style, link, noscript, template"
|
|
308
349
|
)) {
|
|
309
350
|
el.remove();
|
|
310
351
|
}
|
|
352
|
+
for (const link of clone.querySelectorAll<HTMLAnchorElement>("a[href]")) {
|
|
353
|
+
if (!link.getAttribute("href")?.startsWith("#")) {
|
|
354
|
+
link.setAttribute("href", link.href);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// The generator fetches every image into the book, and one it can't fetch
|
|
358
|
+
// (a cross-origin image served without CORS headers, a 404) fails the
|
|
359
|
+
// whole export. Try each first, and leave out the ones that fail.
|
|
360
|
+
await Promise.all(
|
|
361
|
+
[...clone.querySelectorAll("img")].map(async (img) => {
|
|
362
|
+
if (!img.getAttribute("src")) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
const response = await fetch(img.src);
|
|
367
|
+
if (!response.ok) {
|
|
368
|
+
throw new Error(`HTTP ${response.status}`);
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.warn(
|
|
372
|
+
`[blume] EPUB export left out an image it couldn't fetch: ${img.src}`,
|
|
373
|
+
error
|
|
374
|
+
);
|
|
375
|
+
img.remove();
|
|
376
|
+
}
|
|
377
|
+
})
|
|
378
|
+
);
|
|
311
379
|
for (const pre of clone.querySelectorAll("pre")) {
|
|
312
380
|
const code = document.createElement("code");
|
|
313
381
|
code.textContent = pre.textContent ?? "";
|
|
@@ -339,9 +407,11 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
339
407
|
menu.classList.toggle("mb-1", flipUp);
|
|
340
408
|
};
|
|
341
409
|
|
|
410
|
+
// Only the rail's floating dropdowns need placing; a group open in place in
|
|
411
|
+
// the mobile dropdown is not one (see `placeActions`).
|
|
342
412
|
const openDropdown = () =>
|
|
343
413
|
document.querySelector<HTMLDetailsElement>(
|
|
344
|
-
"[data-blume-page-actions] details[open]"
|
|
414
|
+
"[data-blume-page-actions] details[data-blume-dropdown][open]"
|
|
345
415
|
);
|
|
346
416
|
|
|
347
417
|
// rAF-coalesced so a live resize drag re-reads layout once per frame, not
|
|
@@ -362,6 +432,32 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
362
432
|
// dropdown behavior (also the header language switcher and nav selectors).
|
|
363
433
|
installDropdownDismiss();
|
|
364
434
|
|
|
435
|
+
// The page renders its one actions block in the right rail, which only
|
|
436
|
+
// shows at `xl`. Below that the block moves into the mobile "On this page"
|
|
437
|
+
// dropdown, into the empty mount the layout passes it as children, and
|
|
438
|
+
// back at `xl`; a moved node keeps its listeners, so nothing rebinds. In
|
|
439
|
+
// the dropdown its groups expand in place, so they drop out of the shared
|
|
440
|
+
// light-dismiss and the placement here (both keyed on
|
|
441
|
+
// `data-blume-dropdown`) until the block is back in the rail, and the
|
|
442
|
+
// styles above restyle it from the mount.
|
|
443
|
+
const railQuery = window.matchMedia("(min-width: 80rem)");
|
|
444
|
+
const placeActions = () => {
|
|
445
|
+
const root = document.querySelector("[data-blume-page-actions]");
|
|
446
|
+
const rail = railQuery.matches;
|
|
447
|
+
const home = document.querySelector(
|
|
448
|
+
rail ? "[data-blume-toc]" : "[data-blume-page-actions-mount]"
|
|
449
|
+
);
|
|
450
|
+
if (!(root && home) || root.parentElement === home) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
// A group left open would reopen as the other kind of menu.
|
|
454
|
+
for (const details of root.querySelectorAll("details")) {
|
|
455
|
+
details.open = false;
|
|
456
|
+
details.toggleAttribute("data-blume-dropdown", rail);
|
|
457
|
+
}
|
|
458
|
+
home.append(root);
|
|
459
|
+
};
|
|
460
|
+
|
|
365
461
|
// Per-page setup, run on the initial load and again after every
|
|
366
462
|
// client-router swap: the swap rebuilds the actions menu from
|
|
367
463
|
// server-rendered markup, so every handler below binds to fresh elements.
|
|
@@ -384,7 +480,9 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
384
480
|
other.open = false;
|
|
385
481
|
}
|
|
386
482
|
}
|
|
387
|
-
|
|
483
|
+
if (details.hasAttribute("data-blume-dropdown")) {
|
|
484
|
+
placeMenu(details);
|
|
485
|
+
}
|
|
388
486
|
});
|
|
389
487
|
}
|
|
390
488
|
|
|
@@ -537,10 +635,24 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
537
635
|
const epubModule = await loadEpub();
|
|
538
636
|
const epub = epubModule.default?.default ?? epubModule.default;
|
|
539
637
|
// The article HTML already opens with the page's <h1>, so don't let the
|
|
540
|
-
// generator prepend a second chapter-title heading.
|
|
638
|
+
// generator prepend a second chapter-title heading. The book's
|
|
639
|
+
// language is the article's (a fallback page's content locale, else
|
|
640
|
+
// the page's), and the site is its author and publisher; left out,
|
|
641
|
+
// the generator says English and "anonymous". Its defaults are spread
|
|
642
|
+
// under these options, so an unknown value is omitted, not undefined.
|
|
643
|
+
const site = document
|
|
644
|
+
.querySelector('meta[property="og:site_name"]')
|
|
645
|
+
?.getAttribute("content");
|
|
646
|
+
const lang = article.closest("[lang]")?.getAttribute("lang");
|
|
541
647
|
const result = await epub(
|
|
542
|
-
{
|
|
543
|
-
|
|
648
|
+
{
|
|
649
|
+
css: EPUB_STYLES,
|
|
650
|
+
prependChapterTitles: false,
|
|
651
|
+
title,
|
|
652
|
+
...(site && { author: site, publisher: site }),
|
|
653
|
+
...(lang && { lang }),
|
|
654
|
+
},
|
|
655
|
+
[{ content: await buildEpubChapterHtml(article), title }]
|
|
544
656
|
);
|
|
545
657
|
const blob =
|
|
546
658
|
result instanceof Blob
|
|
@@ -572,5 +684,8 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
|
|
|
572
684
|
};
|
|
573
685
|
|
|
574
686
|
initPageActions();
|
|
687
|
+
placeActions();
|
|
575
688
|
document.addEventListener("astro:after-swap", initPageActions);
|
|
689
|
+
document.addEventListener("astro:after-swap", placeActions);
|
|
690
|
+
railQuery.addEventListener("change", placeActions);
|
|
576
691
|
</script>
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
---
|
|
2
|
+
import data from "blume:data";
|
|
3
|
+
|
|
2
4
|
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
3
5
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
4
6
|
import Icon from "../Icon.astro";
|
|
7
|
+
import { COMMENT_MAX_LENGTH } from "./page-feedback.ts";
|
|
5
8
|
|
|
6
9
|
interface Props {
|
|
10
|
+
/** Ask for a written comment after the rating (`feedback.comments`). */
|
|
11
|
+
comments?: boolean;
|
|
7
12
|
/** Localized feedback labels. */
|
|
8
13
|
strings?: UIStrings["feedback"];
|
|
9
14
|
}
|
|
10
15
|
|
|
11
|
-
const { strings } = Astro.props;
|
|
16
|
+
const { comments = false, strings } = Astro.props;
|
|
17
|
+
// Answers go through analytics, so under a consent layer the rating waits,
|
|
18
|
+
// hidden, until the reader allows analytics (`page-feedback.ts` shows it).
|
|
19
|
+
const waitsForConsent = data.config.consent !== null;
|
|
12
20
|
// Merge over the English baseline per key (rather than `strings ?? …`) so a
|
|
13
21
|
// partial — or empty `{}` — strings object still resolves every label to a
|
|
14
22
|
// default, matching the pattern PageActions and Search use.
|
|
@@ -20,61 +28,58 @@ const buttonClass =
|
|
|
20
28
|
|
|
21
29
|
<section
|
|
22
30
|
aria-label={f.question}
|
|
23
|
-
class="mx-auto mt-12
|
|
31
|
+
class="mx-auto mt-12 max-w-content border-border border-t pt-6"
|
|
24
32
|
data-blume-page-feedback
|
|
33
|
+
hidden={waitsForConsent}
|
|
25
34
|
>
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
<div class="flex items-center justify-between gap-4">
|
|
36
|
+
<p class="text-muted-foreground text-sm">{f.question}</p>
|
|
37
|
+
<div class="flex items-center gap-2" data-feedback-actions>
|
|
38
|
+
<button class={buttonClass} data-feedback-value="yes" type="button">
|
|
39
|
+
<Icon name="thumbs-up" size={16} />
|
|
40
|
+
{f.yes}
|
|
41
|
+
</button>
|
|
42
|
+
<button class={buttonClass} data-feedback-value="no" type="button">
|
|
43
|
+
<Icon name="thumbs-down" size={16} />
|
|
44
|
+
{f.no}
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
<p
|
|
48
|
+
class="hidden text-muted-foreground text-sm focus:outline-none"
|
|
49
|
+
data-feedback-thanks
|
|
50
|
+
role="status"
|
|
51
|
+
tabindex="-1"
|
|
52
|
+
>
|
|
53
|
+
{f.thanks}
|
|
54
|
+
</p>
|
|
36
55
|
</div>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
{
|
|
57
|
+
comments && (
|
|
58
|
+
<form class="mt-4 grid gap-2" data-feedback-form hidden>
|
|
59
|
+
<label class="text-muted-foreground text-sm" for="blume-feedback-comment">
|
|
60
|
+
{f.comment}
|
|
61
|
+
</label>
|
|
62
|
+
<textarea
|
|
63
|
+
class="w-full resize-y rounded-blume border border-border bg-background px-3 py-2 text-foreground text-sm outline-none transition-colors focus:border-foreground"
|
|
64
|
+
id="blume-feedback-comment"
|
|
65
|
+
maxlength={COMMENT_MAX_LENGTH}
|
|
66
|
+
name="comment"
|
|
67
|
+
rows="3"
|
|
68
|
+
></textarea>
|
|
69
|
+
<div>
|
|
70
|
+
<button class={buttonClass} type="submit">
|
|
71
|
+
{f.send}
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
</form>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
45
77
|
</section>
|
|
46
78
|
|
|
47
79
|
<script>
|
|
48
|
-
import {
|
|
49
|
-
|
|
50
|
-
// Run on the initial load and again after every client-router swap, which
|
|
51
|
-
// rebuilds the rating widget from server-rendered markup — the handlers
|
|
52
|
-
// below always bind to the freshly swapped-in elements.
|
|
53
|
-
const initFeedback = () => {
|
|
54
|
-
const root = document.querySelector("[data-blume-page-feedback]");
|
|
55
|
-
if (!root) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const actions = root.querySelector("[data-feedback-actions]");
|
|
59
|
-
const thanks = root.querySelector("[data-feedback-thanks]");
|
|
60
|
-
for (const button of root.querySelectorAll("[data-feedback-value]")) {
|
|
61
|
-
button.addEventListener("click", () => {
|
|
62
|
-
track("feedback", {
|
|
63
|
-
helpful: button.getAttribute("data-feedback-value") ?? "",
|
|
64
|
-
path: location.pathname,
|
|
65
|
-
title: document.title,
|
|
66
|
-
});
|
|
67
|
-
actions?.classList.add("hidden");
|
|
68
|
-
thanks?.classList.remove("hidden");
|
|
69
|
-
// The clicked button just disappeared under focus; hand focus to the
|
|
70
|
-
// (live) thanks line so the reader isn't dropped at the top of the page.
|
|
71
|
-
if (thanks instanceof HTMLElement) {
|
|
72
|
-
thanks.focus();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
};
|
|
80
|
+
import { initFeedback, syncFeedbackVisibility } from "./page-feedback.ts";
|
|
77
81
|
|
|
78
82
|
initFeedback();
|
|
79
83
|
document.addEventListener("astro:after-swap", initFeedback);
|
|
84
|
+
addEventListener("blume:consent", syncFeedbackVisibility);
|
|
80
85
|
</script>
|
|
@@ -5,8 +5,8 @@ import { createIconSprite } from "../icon-sprite.ts";
|
|
|
5
5
|
// page that wants Blume's chrome (document shell, header, theme, fonts) without
|
|
6
6
|
// the docs sidebar + prose + TOC grid that RootLayout hard-codes. It renders the
|
|
7
7
|
// `<head>`, theme/banner pre-paint scripts, banner, and header, then a single
|
|
8
|
-
// full-width `<slot />` for the body
|
|
9
|
-
//
|
|
8
|
+
// full-width `<slot />` for the body, then an optional `footer` slot, or the
|
|
9
|
+
// site footer `footer` configures when the page fills no slot. Import it into a custom page and pull props from `blume:data`:
|
|
10
10
|
//
|
|
11
11
|
// import PageLayout from "blume/components/layout/PageLayout.astro";
|
|
12
12
|
// import data from "blume:data";
|
|
@@ -40,11 +40,18 @@ import { buildStructuredData } from "../../seo/jsonld.ts";
|
|
|
40
40
|
import { normalizeXHandle } from "../../seo/x-handle.ts";
|
|
41
41
|
import Icon from "../Icon.astro";
|
|
42
42
|
import { stripBase, withBase, withMountedBase } from "../islands/base-path.ts";
|
|
43
|
+
import { routeSetFor } from "../../core/locale-links.ts";
|
|
44
|
+
import {
|
|
45
|
+
browserLanguageTargets,
|
|
46
|
+
LOCALE_REDIRECT_SCRIPT,
|
|
47
|
+
} from "./locale-redirect.ts";
|
|
43
48
|
import { pageDirection, pageLocale } from "./page-locale.ts";
|
|
44
49
|
import { searchLocaleFor } from "./search-locale.ts";
|
|
45
50
|
import "blume:theme";
|
|
46
51
|
import { ClientRouter } from "astro:transitions";
|
|
47
52
|
import Analytics from "./Analytics.astro";
|
|
53
|
+
import ConsentBanner from "./ConsentBanner.astro";
|
|
54
|
+
import ConsentHead from "./ConsentHead.astro";
|
|
48
55
|
import Banner from "./Banner.astro";
|
|
49
56
|
import DiscoveryLinks from "./DiscoveryLinks.astro";
|
|
50
57
|
import Favicon from "./Favicon.astro";
|
|
@@ -57,6 +64,9 @@ import {
|
|
|
57
64
|
import Header from "./Header.astro";
|
|
58
65
|
import { currentTabForRoute } from "./nav-utils.ts";
|
|
59
66
|
import NavTabMenu from "./NavTabMenu.astro";
|
|
67
|
+
import DrawerSwitchers from "./DrawerSwitchers.astro";
|
|
68
|
+
import SiteFooter from "./SiteFooter.astro";
|
|
69
|
+
import SiteMetatags from "./SiteMetatags.astro";
|
|
60
70
|
import WebMcp from "./WebMcp.astro";
|
|
61
71
|
|
|
62
72
|
interface Props {
|
|
@@ -195,6 +205,8 @@ const clientDataJson = clientData
|
|
|
195
205
|
: null;
|
|
196
206
|
|
|
197
207
|
const strings = ui ?? EN_UI;
|
|
208
|
+
// The consent adapter (`consent`), site-wide: analytics waits for the reader.
|
|
209
|
+
const consent = data.config.consent;
|
|
198
210
|
// Merge over the English defaults so a label missing from a translation (or
|
|
199
211
|
// from a not-yet-regenerated snapshot) still renders instead of coming out
|
|
200
212
|
// blank — the PageActions pattern.
|
|
@@ -291,6 +303,25 @@ const bannerKey = banner?.dismissible ? banner.key : null;
|
|
|
291
303
|
// Start the page's icon sprite before anything renders an icon (see
|
|
292
304
|
// icon-sprite.ts); <IconSprite /> at the end of the body emits it.
|
|
293
305
|
createIconSprite(Astro.locals);
|
|
306
|
+
|
|
307
|
+
// A visitor landing on the default language's home page goes to their
|
|
308
|
+
// browser's language (`i18n.routeByBrowserLanguage`, see locale-redirect.ts).
|
|
309
|
+
const localeTargets = browserLanguageTargets({
|
|
310
|
+
basePath: data.config.basePath,
|
|
311
|
+
i18n: data.config.i18n,
|
|
312
|
+
route,
|
|
313
|
+
routes: routeSetFor(data.routes),
|
|
314
|
+
});
|
|
315
|
+
const localeRedirect = localeTargets
|
|
316
|
+
? JSON.stringify(
|
|
317
|
+
Object.fromEntries(
|
|
318
|
+
Object.entries(localeTargets).map(([code, path]) => [
|
|
319
|
+
code,
|
|
320
|
+
withMountedBase(path),
|
|
321
|
+
])
|
|
322
|
+
)
|
|
323
|
+
)
|
|
324
|
+
: null;
|
|
294
325
|
---
|
|
295
326
|
|
|
296
327
|
<!doctype html>
|
|
@@ -298,6 +329,16 @@ createIconSprite(Astro.locals);
|
|
|
298
329
|
<head>
|
|
299
330
|
<meta charset="utf-8" />
|
|
300
331
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
332
|
+
{
|
|
333
|
+
localeRedirect && (
|
|
334
|
+
<script
|
|
335
|
+
data-default={data.config.i18n?.defaultLocale}
|
|
336
|
+
data-targets={localeRedirect}
|
|
337
|
+
is:inline
|
|
338
|
+
set:html={LOCALE_REDIRECT_SCRIPT}
|
|
339
|
+
/>
|
|
340
|
+
)
|
|
341
|
+
}
|
|
301
342
|
{/* Client-side navigation, matching RootLayout: same-origin link clicks
|
|
302
343
|
swap the DOM in place instead of tearing the document down, so no
|
|
303
344
|
browser paints a blank frame between pages. */}
|
|
@@ -363,6 +404,7 @@ createIconSprite(Astro.locals);
|
|
|
363
404
|
{description && <meta content={description} name="twitter:description" />}
|
|
364
405
|
{xSite && <meta content={xSite} name="twitter:site" />}
|
|
365
406
|
{xCreator && <meta content={xCreator} name="twitter:creator" />}
|
|
407
|
+
<SiteMetatags tags={data.config.metatags} />
|
|
366
408
|
{/* Agent discovery on every page, so a custom page built on PageLayout is
|
|
367
409
|
covered without passing a prop, the same way the docs pages are. */}
|
|
368
410
|
<DiscoveryLinks discovery={discovery} markdownMirror={markdownMirror} />
|
|
@@ -381,6 +423,7 @@ createIconSprite(Astro.locals);
|
|
|
381
423
|
)
|
|
382
424
|
}
|
|
383
425
|
<script data-mode={themeMode} is:inline set:html={THEME_INIT_SCRIPT} />
|
|
426
|
+
{consent && <ConsentHead consent={consent} />}
|
|
384
427
|
<Analytics analytics={analytics} />
|
|
385
428
|
</head>
|
|
386
429
|
<body class="bg-background font-sans text-foreground antialiased">
|
|
@@ -409,8 +452,27 @@ createIconSprite(Astro.locals);
|
|
|
409
452
|
transparent={transparentHeader}
|
|
410
453
|
/>
|
|
411
454
|
<main id="blume-content"><slot /></main>
|
|
412
|
-
|
|
455
|
+
{
|
|
456
|
+
Astro.slots.has("footer") ? (
|
|
457
|
+
<slot name="footer" />
|
|
458
|
+
) : (
|
|
459
|
+
<SiteFooter
|
|
460
|
+
footer={data.config.footer}
|
|
461
|
+
locale={locale}
|
|
462
|
+
navigation={navigation}
|
|
463
|
+
ui={strings}
|
|
464
|
+
/>
|
|
465
|
+
)
|
|
466
|
+
}
|
|
413
467
|
<WebMcp />
|
|
468
|
+
{
|
|
469
|
+
consent?.kind === "native" && (
|
|
470
|
+
<ConsentBanner
|
|
471
|
+
policy={consent.options.policy}
|
|
472
|
+
strings={strings.consent}
|
|
473
|
+
/>
|
|
474
|
+
)
|
|
475
|
+
}
|
|
414
476
|
{
|
|
415
477
|
// Mobile nav for chrome-only pages: the header's inline tab bar is hidden
|
|
416
478
|
// below `lg` and there's no sidebar drawer, so surface the tabs in a
|
|
@@ -425,6 +487,12 @@ createIconSprite(Astro.locals);
|
|
|
425
487
|
data-blume-nav-drawer
|
|
426
488
|
id="blume-nav-drawer"
|
|
427
489
|
>
|
|
490
|
+
<DrawerSwitchers
|
|
491
|
+
localeSwitch={localeSwitch}
|
|
492
|
+
route={route}
|
|
493
|
+
selectors={navigation.selectors}
|
|
494
|
+
switcherStrings={strings.languageSwitcher}
|
|
495
|
+
/>
|
|
428
496
|
<nav aria-label={navStrings.sections}>
|
|
429
497
|
<ul class="m-0 list-none p-0">
|
|
430
498
|
{navigation.tabs.map((tab) => (
|