blume 2.1.0 → 2.1.2
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 +110 -0
- 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-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
- package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
- package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
- package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
- package/dist/cli/chunk-124fvmzq.js.map +220 -0
- package/dist/cli/chunk-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
- package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
- package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
- package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
- package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
- package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
- package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
- package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
- package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
- package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
- package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
- package/dist/cli/chunk-60hz265r.js.map +10 -0
- package/dist/cli/chunk-74wfhv79.js +65 -0
- package/dist/cli/chunk-74wfhv79.js.map +10 -0
- package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
- package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
- package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
- package/dist/cli/chunk-ave1wcbr.js.map +14 -0
- package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
- package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
- package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
- package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
- package/dist/cli/chunk-ckrcm08h.js +324 -0
- package/dist/cli/chunk-ckrcm08h.js.map +14 -0
- package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
- package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
- package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
- package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
- package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
- package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
- package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
- package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
- package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
- package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
- package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
- package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
- package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
- package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
- package/dist/cli/chunk-j1x92mx9.js.map +10 -0
- package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
- package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
- package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
- package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
- package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
- package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
- package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
- package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
- package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
- package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
- package/dist/cli/chunk-twy7psdz.js.map +10 -0
- package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
- package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
- package/dist/cli/index.js +24 -19
- package/dist/cli/index.js.map +3 -3
- package/dist/types/ai/ask-context.d.ts +6 -2
- package/dist/types/ai/ask.d.ts +24 -5
- package/dist/types/ai/component-markdown.d.ts +2 -0
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/analytics/clics.d.ts +37 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +18 -0
- package/dist/types/astro/integration.d.ts +11 -4
- package/dist/types/astro/markdown-negotiation.d.ts +3 -0
- package/dist/types/astro/pages.d.ts +10 -0
- package/dist/types/cli/init/scaffold.d.ts +1 -1
- package/dist/types/components/content/api-field.d.ts +4 -1
- package/dist/types/components/content/api-page.d.ts +6 -1
- package/dist/types/components/openapi/code-samples.d.ts +29 -0
- package/dist/types/components/openapi/helpers.d.ts +22 -1
- package/dist/types/components/openapi/security.d.ts +10 -0
- package/dist/types/components/openapi/snippets.d.ts +60 -0
- package/dist/types/core/changelog-index.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +37 -6
- package/dist/types/core/data.d.ts +4 -0
- package/dist/types/core/i18n-ui.d.ts +2 -0
- package/dist/types/core/links.d.ts +8 -0
- package/dist/types/core/powered-by.d.ts +12 -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 +11 -3
- package/dist/types/core/schema.d.ts +228 -3
- 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/lower.d.ts +10 -2
- package/dist/types/core/sources/normalize.d.ts +20 -1
- package/dist/types/core/sources/notion.d.ts +8 -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 +17 -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/cloudflare-negotiation.d.ts +5 -2
- package/dist/types/deploy/node-headers.d.ts +12 -4
- package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
- 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 +21 -2
- package/dist/types/deploy/redirects.d.ts +17 -1
- package/dist/types/deploy/sitemap.d.ts +2 -1
- package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
- package/dist/types/narration/build.d.ts +34 -12
- package/dist/types/narration/provider.d.ts +96 -8
- 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 +15 -4
- package/dist/types/openapi/parse.d.ts +3 -0
- package/dist/types/openapi/references.d.ts +16 -0
- package/dist/types/openapi/sentence.d.ts +11 -1
- package/dist/types/ratelimit/index.d.ts +1 -1
- package/dist/types/ratelimit/schema.d.ts +8 -0
- package/dist/types/ratelimit/upstash.d.ts +39 -14
- package/dist/types/reference/graphql.d.ts +76 -1
- package/dist/types/reference/index.d.ts +1 -1
- package/dist/types/reference/openapi.d.ts +2 -2
- package/dist/types/reference/options.d.ts +37 -1
- package/dist/types/reference/schema.d.ts +84 -0
- 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/orama-index.d.ts +9 -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/jsonld.d.ts +11 -6
- package/dist/types/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/notion.d.ts +8 -0
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +63 -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 +59 -13
- package/docs/03-upgrading.mdx +2 -1
- package/docs/04-migrating.mdx +9 -9
- package/docs/08-faq.mdx +12 -12
- package/docs/advanced/changelog.mdx +30 -2
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +30 -7
- package/docs/cli/doctor.mdx +6 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/translate.mdx +1 -0
- package/docs/cli/validate.mdx +4 -3
- package/docs/configuration/analytics.mdx +19 -1
- package/docs/configuration/assistant.mdx +6 -2
- package/docs/configuration/customization.mdx +4 -2
- package/docs/configuration/export.mdx +5 -3
- package/docs/configuration/index.mdx +4 -2
- package/docs/configuration/narration.mdx +43 -6
- package/docs/configuration/rate-limiting.mdx +23 -2
- package/docs/configuration/search.mdx +37 -14
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -3
- package/docs/content/i18n.mdx +2 -2
- package/docs/content/includes.mdx +1 -1
- package/docs/content/navigation.mdx +8 -6
- package/docs/content/sources/contentful.mdx +9 -4
- package/docs/content/sources/custom.mdx +7 -3
- package/docs/content/sources/github-releases.mdx +12 -2
- package/docs/content/sources/index.mdx +3 -3
- package/docs/content/sources/notion.mdx +17 -2
- package/docs/content/sources/obsidian.mdx +3 -1
- package/docs/content/sources/payload.mdx +5 -2
- package/docs/content/sources/remote-mdx.mdx +6 -0
- package/docs/content/sources/sanity.mdx +4 -2
- package/docs/content/sources/strapi.mdx +5 -2
- package/docs/content/syntax.mdx +2 -2
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/json-api.mdx +2 -0
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +3 -1
- package/docs/discoverability/mcp.mdx +6 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/discoverability/structured-data.mdx +8 -6
- package/docs/references/api-pages.mdx +1 -1
- package/docs/references/asyncapi.mdx +5 -1
- package/docs/references/graphql.mdx +15 -1
- package/docs/references/openapi.mdx +64 -8
- package/package.json +9 -9
- package/skills/blume/SKILL.md +1 -1
- package/skills/blume-migrate/SKILL.md +2 -2
- package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
- package/skills/blume-migrate/references/docusaurus.md +4 -4
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- package/skills/blume-migrate/references/monorepo.md +5 -5
- package/skills/blume-migrate/references/nextra.md +8 -5
- package/skills/blume-migrate/references/starlight.md +9 -7
- package/src/ai/ask-context.ts +33 -3
- package/src/ai/ask-limits.ts +13 -0
- package/src/ai/ask.ts +24 -6
- package/src/ai/changelog-markdown.ts +11 -6
- package/src/ai/component-markdown.ts +1 -1
- package/src/ai/cors.ts +12 -3
- package/src/ai/mcp/query.ts +14 -6
- package/src/ai/openapi-components.ts +28 -4
- package/src/ai/relative-links.ts +4 -38
- package/src/analytics/clics.ts +58 -0
- package/src/analytics/head.ts +5 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +91 -56
- package/src/astro/integration.ts +72 -22
- package/src/astro/markdown-negotiation.ts +3 -0
- package/src/astro/module-types.ts +3 -1
- package/src/astro/pages.ts +23 -0
- package/src/astro/render-deps.ts +10 -5
- package/src/astro/runtime-deps.ts +23 -2
- package/src/astro/templates.ts +129 -81
- package/src/audit/catalog.ts +19 -3
- package/src/audit/checks/links.ts +14 -1
- 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/run.ts +3 -0
- package/src/audit/types.ts +2 -0
- package/src/audit/url.ts +14 -5
- package/src/blume-modules.d.ts +5 -1
- package/src/cli/args.ts +51 -0
- package/src/cli/build-failure.ts +23 -0
- package/src/cli/commands/audit.ts +10 -1
- package/src/cli/commands/dev.ts +18 -2
- package/src/cli/commands/doctor.ts +45 -12
- package/src/cli/commands/eval.ts +36 -4
- package/src/cli/commands/init.ts +6 -4
- package/src/cli/commands/validate.ts +10 -1
- package/src/cli/init/scaffold.ts +9 -6
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +3 -2
- package/src/components/content/ApiPlayground.astro +6 -2
- package/src/components/content/api-field.ts +4 -1
- package/src/components/content/api-page.ts +31 -7
- package/src/components/content/mermaid-element.ts +15 -1
- package/src/components/content/view-switcher.ts +7 -4
- package/src/components/islands/hooks.ts +27 -1
- package/src/components/layout/NavTree.astro +49 -5
- package/src/components/layout/NavTreeScript.astro +18 -0
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/PageLayout.astro +13 -3
- package/src/components/layout/RootLayout.astro +27 -14
- package/src/components/layout/Search.astro +17 -3
- package/src/components/layout/TableOfContents.astro +24 -17
- package/src/components/layout/analytics-client.ts +8 -8
- package/src/components/layout/head-scripts.ts +6 -1
- package/src/components/layout/search/algolia.ts +10 -1
- package/src/components/layout/search/analytics.ts +42 -14
- package/src/components/layout/search/endpoint.ts +48 -5
- package/src/components/layout/search/pagefind.ts +114 -5
- package/src/components/layout/search/types.ts +12 -0
- package/src/components/layout/search/typesense.ts +18 -6
- 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 +24 -1
- package/src/components/openapi/ParametersTable.astro +8 -4
- package/src/components/openapi/Playground.astro +7 -1
- package/src/components/openapi/RequestBody.astro +19 -2
- package/src/components/openapi/Responses.astro +1 -0
- package/src/components/openapi/SchemaTable.astro +15 -3
- package/src/components/openapi/async-snippets.ts +61 -5
- package/src/components/openapi/async.ts +123 -29
- 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 +77 -58
- package/src/components/openapi/playground-schema.ts +1 -1
- package/src/components/openapi/schema-tree.ts +44 -14
- package/src/components/openapi/security.ts +10 -0
- package/src/components/openapi/snippets.ts +5 -3
- package/src/components/openapi/validate-json.ts +4 -3
- package/src/components/powered-by-middleware.ts +33 -0
- package/src/core/changelog-index.ts +31 -1
- package/src/core/component-diagnostics.ts +53 -0
- package/src/core/component-overrides.ts +32 -0
- package/src/core/config-input.ts +42 -6
- package/src/core/data.ts +2 -0
- package/src/core/diagnostics.ts +5 -0
- package/src/core/i18n-ui.ts +2 -0
- package/src/core/includes.ts +1 -1
- package/src/core/links.ts +47 -17
- package/src/core/powered-by.ts +12 -0
- package/src/core/probe.ts +26 -6
- package/src/core/project-graph.ts +59 -18
- package/src/core/redirect-patterns.ts +36 -7
- package/src/core/schema.ts +26 -6
- 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/lower.ts +19 -3
- package/src/core/sources/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +227 -41
- package/src/core/sources/notion.ts +34 -10
- package/src/core/sources/obsidian.ts +16 -3
- package/src/core/sources/payload.ts +38 -15
- 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 +18 -0
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +1 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +1 -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 +1 -0
- package/src/core/ui-packs/el.ts +1 -0
- package/src/core/ui-packs/es.ts +1 -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 +1 -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 +1 -0
- package/src/core/ui-packs/hu.ts +1 -0
- package/src/core/ui-packs/id.ts +1 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +1 -0
- package/src/core/ui-packs/ko.ts +1 -0
- package/src/core/ui-packs/nl.ts +1 -0
- package/src/core/ui-packs/no.ts +1 -0
- package/src/core/ui-packs/pl.ts +1 -0
- package/src/core/ui-packs/pt-br.ts +1 -0
- package/src/core/ui-packs/pt.ts +1 -0
- package/src/core/ui-packs/ro.ts +1 -0
- package/src/core/ui-packs/ru.ts +1 -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 +1 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +1 -0
- package/src/core/ui-packs/uk.ts +1 -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/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 +2 -1
- package/src/deploy/cloudflare-negotiation.ts +36 -7
- package/src/deploy/headers.ts +15 -1
- package/src/deploy/node-headers.ts +32 -20
- package/src/deploy/platforms/cloudflare.ts +14 -9
- package/src/deploy/platforms/netlify.ts +2 -1
- package/src/deploy/platforms/node.ts +12 -2
- package/src/deploy/platforms/types.ts +21 -7
- package/src/deploy/platforms/vercel.ts +115 -17
- package/src/deploy/redirects.ts +66 -6
- package/src/deploy/rss.ts +3 -1
- package/src/deploy/sitemap.ts +39 -5
- package/src/deploy/vercel-negotiation.ts +325 -103
- 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/base-links.ts +15 -14
- package/src/markdown/package-commands.ts +9 -8
- package/src/markdown/relative-links.ts +3 -2
- package/src/narration/build.ts +218 -40
- package/src/narration/provider.ts +89 -9
- 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 +15 -3
- package/src/openapi/parse.ts +127 -2
- package/src/openapi/references.ts +43 -3
- package/src/openapi/render-mdx.ts +68 -8
- package/src/openapi/sentence.ts +29 -2
- package/src/openapi/source.ts +39 -14
- package/src/ratelimit/index.ts +1 -1
- package/src/ratelimit/runtime.ts +37 -19
- package/src/ratelimit/upstash.ts +40 -10
- package/src/reference/graphql.ts +15 -1
- package/src/reference/index.ts +1 -0
- package/src/reference/openapi.ts +2 -2
- package/src/reference/options.ts +29 -1
- package/src/registry/eject.ts +8 -2
- 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/orama-index.ts +163 -31
- package/src/search/source-pages.ts +29 -0
- package/src/search/sync/algolia.ts +99 -9
- package/src/search/sync/index.ts +52 -13
- package/src/search/sync/typesense.ts +72 -27
- package/src/seo/jsonld.ts +38 -20
- package/src/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/notion.ts +7 -0
- package/src/sources/shared.ts +3 -0
- package/src/theme/color.ts +9 -10
- package/src/theme/entry.ts +43 -6
- package/src/translate/validate.ts +23 -1
- package/dist/cli/chunk-0gz36akr.js.map +0 -12
- package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
- package/dist/cli/chunk-43zcf4gz.js.map +0 -36
- package/dist/cli/chunk-4ejxbzee.js +0 -39
- package/dist/cli/chunk-4ejxbzee.js.map +0 -10
- package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
- package/dist/cli/chunk-713mngdq.js.map +0 -14
- package/dist/cli/chunk-9xr4dgee.js.map +0 -10
- package/dist/cli/chunk-amrb6hgh.js +0 -145
- package/dist/cli/chunk-amrb6hgh.js.map +0 -11
- package/dist/cli/chunk-nd1821sk.js.map +0 -10
- package/dist/cli/chunk-rekqz4qg.js.map +0 -10
- package/dist/cli/chunk-ta27feev.js.map +0 -10
- package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
- package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
- package/dist/cli/chunk-zf748avv.js.map +0 -37
- /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
- /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
- /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
- /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
- /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
- /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
- /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
- /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
- /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
- /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
- /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
- /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
- /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
- /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
- /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
- /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
- /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
|
@@ -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>
|
|
@@ -126,10 +126,17 @@ interface Props {
|
|
|
126
126
|
canonical?: string | null;
|
|
127
127
|
/**
|
|
128
128
|
* Emit schema.org JSON-LD for this page (`data.config.structuredData`) — a
|
|
129
|
-
* WebSite node, plus
|
|
130
|
-
* matching RootLayout.
|
|
129
|
+
* WebSite node, plus a WebPage node on the homepage and an article node on
|
|
130
|
+
* other routes. Defaults to on, matching RootLayout.
|
|
131
131
|
*/
|
|
132
132
|
structuredDataEnabled?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* When the page last changed, emitted as the JSON-LD `dateModified`. A
|
|
135
|
+
* custom page has no content file to date, so pass it by hand; without a
|
|
136
|
+
* machine-readable date, search engines take whatever other date the page
|
|
137
|
+
* shows.
|
|
138
|
+
*/
|
|
139
|
+
lastModified?: string | Date | null;
|
|
133
140
|
/**
|
|
134
141
|
* Which agent-discovery resources exist, advertised as `describedby` head
|
|
135
142
|
* links plus the AI Catalog/ARD pair — the same `DiscoveryLinks` partial
|
|
@@ -184,6 +191,7 @@ const {
|
|
|
184
191
|
ogImageSize,
|
|
185
192
|
canonical,
|
|
186
193
|
structuredDataEnabled,
|
|
194
|
+
lastModified,
|
|
187
195
|
discovery = data.config.discovery,
|
|
188
196
|
x,
|
|
189
197
|
noindex,
|
|
@@ -279,7 +287,8 @@ const xSite = normalizeXHandle(x?.handle);
|
|
|
279
287
|
const xCreator = normalizeXHandle(x?.creator);
|
|
280
288
|
|
|
281
289
|
// JSON-LD, mirroring RootLayout: skipped when disabled or noindexed. A custom
|
|
282
|
-
// page has no breadcrumb trail; the home route
|
|
290
|
+
// page has no breadcrumb trail; the home route is a WebPage, dated by
|
|
291
|
+
// `lastModified` when the page passes one.
|
|
283
292
|
const structuredData =
|
|
284
293
|
structuredDataEnabled === false || noindex
|
|
285
294
|
? null
|
|
@@ -289,6 +298,7 @@ const structuredData =
|
|
|
289
298
|
description,
|
|
290
299
|
identity: data.config.identity,
|
|
291
300
|
locale,
|
|
301
|
+
modified: lastModified,
|
|
292
302
|
route,
|
|
293
303
|
siteName: site.title,
|
|
294
304
|
siteUrl: siteUrl ?? null,
|
|
@@ -432,10 +432,13 @@ const description = page.description ?? site.description;
|
|
|
432
432
|
// pages (and the changelog index, which has no `type`) stay `website`.
|
|
433
433
|
const ogType =
|
|
434
434
|
pageType === "blog" || pageType === "changelog" ? "article" : "website";
|
|
435
|
+
// The last-modified date as ISO 8601, parsed once for every surface that
|
|
436
|
+
// carries it; undefined when absent or unparseable.
|
|
437
|
+
const lastModifiedIso = toIso(lastModified);
|
|
435
438
|
// `article:*` timestamps only belong on an article; a docs page carrying them
|
|
436
439
|
// would be declaring properties its `og:type` doesn't define.
|
|
437
440
|
const articlePublished = ogType === "article" ? toIso(published) : undefined;
|
|
438
|
-
const articleModified = ogType === "article" ?
|
|
441
|
+
const articleModified = ogType === "article" ? lastModifiedIso : undefined;
|
|
439
442
|
// X sizes the card from `twitter:card`: a large image needs the wide variant,
|
|
440
443
|
// and a page with no image still gets a card (the compact `summary`) rather
|
|
441
444
|
// than rendering as a bare link.
|
|
@@ -448,14 +451,12 @@ const xCreator = normalizeXHandle(x?.creator);
|
|
|
448
451
|
|
|
449
452
|
// "Last updated on <date>" — the configured `dateFormat`, in UTC (unless the
|
|
450
453
|
// config names a zone) so it matches the changelog timeline.
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
).format(lastModifiedDate)
|
|
458
|
-
: null;
|
|
454
|
+
const formattedLastModified = lastModifiedIso
|
|
455
|
+
? new Intl.DateTimeFormat(
|
|
456
|
+
locale || "en",
|
|
457
|
+
resolveDateFormatOptions(dateFormat)
|
|
458
|
+
).format(new Date(lastModifiedIso))
|
|
459
|
+
: null;
|
|
459
460
|
|
|
460
461
|
// This page's raw-Markdown mirror, advertised as a `text/markdown` alternate
|
|
461
462
|
// in the head. Content routes always have one (see `markdownRoutePaths`, which
|
|
@@ -521,6 +522,7 @@ const structuredData =
|
|
|
521
522
|
base: import.meta.env.BASE_URL,
|
|
522
523
|
breadcrumbs: crumbs,
|
|
523
524
|
description,
|
|
525
|
+
homeRoute: navigation.root,
|
|
524
526
|
identity: data.config.identity,
|
|
525
527
|
locale,
|
|
526
528
|
modified: lastModified,
|
|
@@ -868,13 +870,20 @@ const localeRedirect = localeTargets
|
|
|
868
870
|
/>
|
|
869
871
|
)
|
|
870
872
|
}
|
|
873
|
+
{/* Below `xl`, where the rail is hidden, the page actions ride in
|
|
874
|
+
the "On this page" dropdown, so it renders wherever the rail
|
|
875
|
+
would, with or without headings. They render once, in the rail:
|
|
876
|
+
the dropdown gets an empty mount the actions script moves them
|
|
877
|
+
into (see PageActions.astro). */}
|
|
871
878
|
{
|
|
872
|
-
|
|
879
|
+
showToc && (
|
|
873
880
|
<TableOfContentsSlot
|
|
874
881
|
headings={tocHeadings}
|
|
875
882
|
title={strings.toc.title}
|
|
876
883
|
variant="mobile"
|
|
877
|
-
|
|
884
|
+
>
|
|
885
|
+
<div data-blume-page-actions-mount />
|
|
886
|
+
</TableOfContentsSlot>
|
|
878
887
|
)
|
|
879
888
|
}
|
|
880
889
|
<PageHeaderSlot headings={tocHeadings} page={page} route={page.route} />
|
|
@@ -911,6 +920,7 @@ const localeRedirect = localeTargets
|
|
|
911
920
|
(apiRail || modeLayout.width === "full") && "[&>*]:max-w-none",
|
|
912
921
|
modeLayout.width === "center" && "[&>*]:max-w-[54rem]",
|
|
913
922
|
]}
|
|
923
|
+
data-blume-page-end
|
|
914
924
|
>
|
|
915
925
|
{directoryGroup && <Directory group={directoryGroup} />}
|
|
916
926
|
<RelatedPages links={relatedItems} title={strings.page.related} />
|
|
@@ -921,7 +931,7 @@ const localeRedirect = localeTargets
|
|
|
921
931
|
/>
|
|
922
932
|
{formattedLastModified && (
|
|
923
933
|
<p class="mx-auto mt-10 max-w-content text-muted-foreground text-sm">
|
|
924
|
-
{strings.page.lastUpdated} {formattedLastModified}
|
|
934
|
+
{strings.page.lastUpdated} <time datetime={lastModifiedIso}>{formattedLastModified}</time>
|
|
925
935
|
</p>
|
|
926
936
|
)}
|
|
927
937
|
{feedback && (
|
|
@@ -1370,10 +1380,13 @@ const localeRedirect = localeTargets
|
|
|
1370
1380
|
}
|
|
1371
1381
|
@media (prefers-reduced-motion: reduce) {
|
|
1372
1382
|
/* html prefix outranks the library's rules regardless of sheet
|
|
1373
|
-
order; its transition declarations carry !important.
|
|
1383
|
+
order; its transition declarations carry !important. Near-instant,
|
|
1384
|
+
never `none`: medium-zoom only finishes opening and closing on the
|
|
1385
|
+
image's `transitionend`, so with no transition a zoomed image
|
|
1386
|
+
ignores Escape, clicks, and scrolls forever. */
|
|
1374
1387
|
html .medium-zoom-image,
|
|
1375
1388
|
html .medium-zoom-overlay {
|
|
1376
|
-
transition:
|
|
1389
|
+
transition-duration: 1ms !important;
|
|
1377
1390
|
}
|
|
1378
1391
|
}
|
|
1379
1392
|
</style>
|
|
@@ -4,6 +4,7 @@ import data from "blume:data";
|
|
|
4
4
|
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
5
5
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
6
6
|
import type { Navigation } from "../../core/types.ts";
|
|
7
|
+
import { scopesLocales } from "../../search/adapters/locale-scope.ts";
|
|
7
8
|
import { scopesVersions } from "../../search/adapters/version-scope.ts";
|
|
8
9
|
import { resolvePopularIconMarkup } from "../../search/popular-icon.ts";
|
|
9
10
|
import Icon from "../Icon.astro";
|
|
@@ -36,9 +37,15 @@ const {
|
|
|
36
37
|
navigation,
|
|
37
38
|
popularPages,
|
|
38
39
|
strings,
|
|
39
|
-
locale,
|
|
40
|
+
locale: viewedLocale,
|
|
40
41
|
version: viewedVersion = null,
|
|
41
42
|
} = Astro.props;
|
|
43
|
+
// Mixedbread searches every language whatever the dialog asks, so on it an
|
|
44
|
+
// "All languages" toggle would change nothing: treat the site as
|
|
45
|
+
// single-language for search instead.
|
|
46
|
+
const locale = scopesLocales(data.config.search.provider)
|
|
47
|
+
? viewedLocale
|
|
48
|
+
: undefined;
|
|
42
49
|
// Pagefind, Orama Cloud, and Mixedbread search every version whatever the
|
|
43
50
|
// dialog asks, so on those an "All versions" toggle would change nothing:
|
|
44
51
|
// treat the site as unversioned for search instead.
|
|
@@ -90,6 +97,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
|
|
|
90
97
|
data-i18n-popular={s.popular}
|
|
91
98
|
data-i18n-results={s.results}
|
|
92
99
|
data-locale={locale || undefined}
|
|
100
|
+
data-redact-queries={data.config.search.analytics.queries ? undefined : ""}
|
|
93
101
|
data-version={version ?? undefined}
|
|
94
102
|
data-versioned={version === null ? undefined : ""}
|
|
95
103
|
>
|
|
@@ -240,6 +248,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
|
|
|
240
248
|
|
|
241
249
|
import { track } from "./analytics-client.ts";
|
|
242
250
|
import { createSearchTracker } from "./search/analytics.ts";
|
|
251
|
+
import type { SearchTracker } from "./search/analytics.ts";
|
|
243
252
|
import { highlight, matchSnippet } from "./search/types.ts";
|
|
244
253
|
import type { SearchFn, SearchHit } from "./search/types.ts";
|
|
245
254
|
|
|
@@ -306,7 +315,8 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
|
|
|
306
315
|
searchClient ??= (async () => {
|
|
307
316
|
try {
|
|
308
317
|
const { createSearch } = await import("blume:search-client");
|
|
309
|
-
|
|
318
|
+
// The reader types each query, so each supersedes the last.
|
|
319
|
+
return await createSearch({ typing: true });
|
|
310
320
|
} catch (error) {
|
|
311
321
|
searchClient = undefined;
|
|
312
322
|
throw error;
|
|
@@ -325,7 +335,7 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
|
|
|
325
335
|
preview!: HTMLElement;
|
|
326
336
|
searchFn: SearchFn | null = null;
|
|
327
337
|
/** Records settled queries and picked results (search/analytics.ts). */
|
|
328
|
-
tracker
|
|
338
|
+
tracker!: SearchTracker;
|
|
329
339
|
loaded = false;
|
|
330
340
|
loadFailed = false;
|
|
331
341
|
/** First-open client/index load still in flight. */
|
|
@@ -419,6 +429,10 @@ const kbd = "rounded border border-border bg-muted px-1 py-0.5 font-mono";
|
|
|
419
429
|
this.message = this.querySelector("[data-blume-search-message]")!;
|
|
420
430
|
this.preview = this.querySelector("[data-blume-search-preview]")!;
|
|
421
431
|
this.assistantEnabled = this.hasAttribute("data-ask");
|
|
432
|
+
// `search.analytics.queries: false` keeps query text off providers.
|
|
433
|
+
this.tracker = createSearchTracker(track, {
|
|
434
|
+
queries: !this.hasAttribute("data-redact-queries"),
|
|
435
|
+
});
|
|
422
436
|
|
|
423
437
|
const popularScript = this.querySelector(
|
|
424
438
|
"[data-blume-search-popular]"
|
|
@@ -15,10 +15,14 @@ interface Props {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const { headings, title, variant } = Astro.props;
|
|
18
|
+
// The mobile dropdown also holds what the layout passes as children (the
|
|
19
|
+
// mount the page actions move into below `xl`, where the rail that renders
|
|
20
|
+
// them is hidden), so a page with actions but no headings still gets it.
|
|
21
|
+
const hasChildren = Astro.slots.has("default");
|
|
18
22
|
---
|
|
19
23
|
|
|
20
24
|
{
|
|
21
|
-
headings.length > 0 && variant === "mobile" && (
|
|
25
|
+
(headings.length > 0 || hasChildren) && variant === "mobile" && (
|
|
22
26
|
<details class="group mx-auto mb-6 max-w-content rounded-blume border border-border xl:hidden">
|
|
23
27
|
<summary class="flex cursor-pointer list-none items-center justify-between gap-2 px-4 py-3 font-medium text-foreground text-sm [&::-webkit-details-marker]:hidden">
|
|
24
28
|
<span>{title}</span>
|
|
@@ -28,23 +32,26 @@ const { headings, title, variant } = Astro.props;
|
|
|
28
32
|
size={16}
|
|
29
33
|
/>
|
|
30
34
|
</summary>
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<a
|
|
39
|
-
class="block rounded-md px-2 py-1.5 text-muted-foreground text-sm transition-colors hover:text-foreground aria-[current=location]:font-medium aria-[current=location]:text-foreground"
|
|
40
|
-
href={`#${heading.slug}`}
|
|
35
|
+
{headings.length > 0 && (
|
|
36
|
+
<blume-toc class="block">
|
|
37
|
+
<ul class="m-0 list-none border-border border-t p-2">
|
|
38
|
+
{headings.map((heading) => (
|
|
39
|
+
<li
|
|
40
|
+
data-depth={heading.depth}
|
|
41
|
+
style={`padding-inline-start:${(heading.depth - 2) * 0.75}rem`}
|
|
41
42
|
>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
<a
|
|
44
|
+
class="block rounded-md px-2 py-1.5 text-muted-foreground text-sm transition-colors hover:text-foreground aria-[current=location]:font-medium aria-[current=location]:text-foreground"
|
|
45
|
+
href={`#${heading.slug}`}
|
|
46
|
+
>
|
|
47
|
+
{heading.text}
|
|
48
|
+
</a>
|
|
49
|
+
</li>
|
|
50
|
+
))}
|
|
51
|
+
</ul>
|
|
52
|
+
</blume-toc>
|
|
53
|
+
)}
|
|
54
|
+
<slot />
|
|
48
55
|
</details>
|
|
49
56
|
)
|
|
50
57
|
}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Send a custom analytics event to every analytics platform configured in
|
|
3
3
|
* `blume.config.ts`. Mirrors the adapters `Analytics.astro` wires
|
|
4
4
|
* (`src/analytics/*`): each one with a custom-event API is called through the
|
|
5
|
-
* global its snippet defines (Cloudflare and
|
|
6
|
-
* to), and the `blume:track` CustomEvent fires unconditionally so a
|
|
7
|
-
* can bridge the event to anything else — a `script()` adapter, say.
|
|
8
|
-
* call no-ops cleanly when a provider isn't present — for example
|
|
9
|
-
* `blume dev`, where `Analytics.astro` injects nothing — and a provider
|
|
10
|
-
* throws (a consent shim that stubs `gtag` with a raise, a broken
|
|
11
|
-
* isolated so it neither starves the providers after it nor
|
|
12
|
-
* feature that reported the event.
|
|
5
|
+
* global its snippet defines (Cloudflare, Clearbit, and Clics have none to
|
|
6
|
+
* forward to), and the `blume:track` CustomEvent fires unconditionally so a
|
|
7
|
+
* project can bridge the event to anything else — a `script()` adapter, say.
|
|
8
|
+
* Every call no-ops cleanly when a provider isn't present — for example
|
|
9
|
+
* during `blume dev`, where `Analytics.astro` injects nothing — and a provider
|
|
10
|
+
* that throws (a consent shim that stubs `gtag` with a raise, a broken
|
|
11
|
+
* snippet) is isolated so it neither starves the providers after it nor
|
|
12
|
+
* surfaces in the feature that reported the event.
|
|
13
13
|
*/
|
|
14
14
|
import { track as vercelTrack } from "@vercel/analytics";
|
|
15
15
|
|
|
@@ -44,8 +44,13 @@
|
|
|
44
44
|
* stored preference instead of aborting before the swap listeners exist, and
|
|
45
45
|
* after a swap the theme the `before-swap` stamp carried over stands, so a
|
|
46
46
|
* toggle the reader made still holds for the rest of their visit.
|
|
47
|
+
*
|
|
48
|
+
* Paper is white, so a dark page prints light: `beforeprint` switches the root
|
|
49
|
+
* to the light theme (every token, Shiki color, and `dark:` utility follows
|
|
50
|
+
* it) and `afterprint` puts the dark theme back. The listeners sit on
|
|
51
|
+
* `window`, so the one registration outlives client-router swaps.
|
|
47
52
|
*/
|
|
48
|
-
export const THEME_INIT_SCRIPT = `(()=>{const m=document.currentScript?.dataset.mode??"system";const apply=()=>{const r=document.documentElement;let s=null;let blocked=false;try{s=localStorage.getItem("blume-theme");}catch{blocked=true;}const sys=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";r.dataset.theme=s??(blocked&&r.dataset.theme?r.dataset.theme:m==="system"?sys:m);};apply();document.addEventListener("astro:before-swap",(e)=>{const t=document.documentElement.dataset.theme;if(t){e.newDocument.documentElement.dataset.theme=t;}});document.addEventListener("astro:after-swap",apply);})();`;
|
|
53
|
+
export const THEME_INIT_SCRIPT = `(()=>{const m=document.currentScript?.dataset.mode??"system";const apply=()=>{const r=document.documentElement;let s=null;let blocked=false;try{s=localStorage.getItem("blume-theme");}catch{blocked=true;}const sys=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";r.dataset.theme=s??(blocked&&r.dataset.theme?r.dataset.theme:m==="system"?sys:m);};apply();document.addEventListener("astro:before-swap",(e)=>{const t=document.documentElement.dataset.theme;if(t){e.newDocument.documentElement.dataset.theme=t;}});document.addEventListener("astro:after-swap",apply);let printed=null;addEventListener("beforeprint",()=>{const r=document.documentElement;if(r.dataset.theme==="dark"){printed="dark";r.dataset.theme="light";}});addEventListener("afterprint",()=>{if(printed){document.documentElement.dataset.theme=printed;printed=null;}});})();`;
|
|
49
54
|
|
|
50
55
|
/**
|
|
51
56
|
* Keep an embedded Scalar reference on Blume's theme. Scalar picks its own
|
|
@@ -48,7 +48,16 @@ export const createSearch = (opts: AlgoliaOptions): SearchFn => {
|
|
|
48
48
|
// shape, so hits returned by that index carry those fields.
|
|
49
49
|
const records =
|
|
50
50
|
first && "hits" in first ? (first.hits as AlgoliaRecord[]) : [];
|
|
51
|
-
|
|
51
|
+
// The sync splits a long page across records that share its `url`. The
|
|
52
|
+
// index's `distinct` returns one of them, unless the site set its own
|
|
53
|
+
// `attributeForDistinct`; either way a page is listed once.
|
|
54
|
+
const pages = new Set<string>();
|
|
55
|
+
const unique = records.filter((record) => {
|
|
56
|
+
const seen = pages.has(record.url);
|
|
57
|
+
pages.add(record.url);
|
|
58
|
+
return !seen;
|
|
59
|
+
});
|
|
60
|
+
const hits = unique.map((record) => ({
|
|
52
61
|
content: record.content ?? "",
|
|
53
62
|
excerpt: highlight(
|
|
54
63
|
excerptFor(record.description ?? "", record.content ?? "", query),
|