blume 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +113 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-6k4ftwze.js → chunk-0gz36akr.js} +18 -2
- package/dist/cli/{chunk-6k4ftwze.js.map → chunk-0gz36akr.js.map} +3 -3
- package/dist/cli/{chunk-zp79m0ts.js → chunk-1n228xqb.js} +2 -2
- package/dist/cli/{chunk-ddndchfr.js → chunk-2f3ndkpw.js} +57 -29
- package/dist/cli/chunk-2f3ndkpw.js.map +14 -0
- package/dist/cli/{chunk-fsmrqk8a.js → chunk-2f7n4qzt.js} +8 -2
- package/dist/cli/{chunk-fsmrqk8a.js.map → chunk-2f7n4qzt.js.map} +3 -3
- package/dist/cli/{chunk-ppzjqwx2.js → chunk-2n4ps1b3.js} +17 -18
- package/dist/cli/{chunk-ppzjqwx2.js.map → chunk-2n4ps1b3.js.map} +3 -3
- package/dist/cli/{chunk-5r8g91qn.js → chunk-43zcf4gz.js} +398 -159
- package/dist/cli/chunk-43zcf4gz.js.map +36 -0
- package/dist/cli/chunk-4ejxbzee.js +39 -0
- package/dist/cli/chunk-4ejxbzee.js.map +10 -0
- package/dist/cli/{chunk-hr8ne106.js → chunk-4h8z9vv5.js} +296 -77
- package/dist/cli/chunk-4h8z9vv5.js.map +16 -0
- package/dist/cli/{chunk-k7pj68a8.js → chunk-5cg2rzgf.js} +18 -16
- package/dist/cli/{chunk-k7pj68a8.js.map → chunk-5cg2rzgf.js.map} +2 -2
- package/dist/cli/{chunk-h2ez8dzb.js → chunk-5ffywsmb.js} +4 -4
- package/dist/cli/{chunk-g698a744.js → chunk-5wj2qwn2.js} +5 -5
- package/dist/cli/chunk-6bm0xpgd.js +18 -0
- package/dist/cli/chunk-6bm0xpgd.js.map +10 -0
- package/dist/cli/{chunk-3yce002v.js → chunk-713mngdq.js} +15 -8
- package/dist/cli/chunk-713mngdq.js.map +14 -0
- package/dist/cli/{chunk-91ws1n6j.js → chunk-9geymqns.js} +17 -15
- package/dist/cli/{chunk-91ws1n6j.js.map → chunk-9geymqns.js.map} +2 -2
- package/dist/cli/{chunk-6dtt0zfn.js → chunk-9xr4dgee.js} +27 -18
- package/dist/cli/chunk-9xr4dgee.js.map +10 -0
- package/dist/cli/{chunk-sqw4ekg1.js → chunk-amrb6hgh.js} +2 -2
- package/dist/cli/chunk-cm89scdv.js +443 -0
- package/dist/cli/chunk-cm89scdv.js.map +11 -0
- package/dist/cli/{chunk-wdrt2k2v.js → chunk-cmtcnh9f.js} +90 -26
- package/dist/cli/chunk-cmtcnh9f.js.map +10 -0
- package/dist/cli/chunk-cy5bm0j0.js +104 -0
- package/dist/cli/chunk-cy5bm0j0.js.map +11 -0
- package/dist/cli/{chunk-7mbqtmgb.js → chunk-e7vvk5ya.js} +21 -12
- package/dist/cli/chunk-e7vvk5ya.js.map +10 -0
- package/dist/cli/{chunk-qkb5a8sa.js → chunk-g2z9x01v.js} +6 -98
- package/dist/cli/chunk-g2z9x01v.js.map +10 -0
- package/dist/cli/{chunk-273ygyr4.js → chunk-ga0pf4aj.js} +7 -7
- package/dist/cli/{chunk-273ygyr4.js.map → chunk-ga0pf4aj.js.map} +3 -3
- package/dist/cli/{chunk-h7k3nq3v.js → chunk-gp84za0h.js} +2 -2
- package/dist/cli/{chunk-hqp2ajnh.js → chunk-h2a48yhd.js} +195 -73
- package/dist/cli/chunk-h2a48yhd.js.map +36 -0
- package/dist/cli/{chunk-pbg5a4s3.js → chunk-jnwj5kjj.js} +71 -47
- package/dist/cli/chunk-jnwj5kjj.js.map +19 -0
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-8g8ytmgx.js → chunk-nd1821sk.js} +21 -19
- package/dist/cli/{chunk-8g8ytmgx.js.map → chunk-nd1821sk.js.map} +2 -2
- package/dist/cli/{chunk-w4bxdvsa.js → chunk-ntv4qrt7.js} +17 -15
- package/dist/cli/{chunk-w4bxdvsa.js.map → chunk-ntv4qrt7.js.map} +2 -2
- package/dist/cli/{chunk-bfwp9vp6.js → chunk-p804f3pe.js} +7 -7
- package/dist/cli/{chunk-4e9b9ra6.js → chunk-py62fewh.js} +6 -6
- package/dist/cli/{chunk-4e9b9ra6.js.map → chunk-py62fewh.js.map} +1 -1
- package/dist/cli/chunk-q3wxxq39.js +126 -0
- package/dist/cli/chunk-q3wxxq39.js.map +11 -0
- package/dist/cli/{chunk-j85scx15.js → chunk-rekqz4qg.js} +142 -46
- package/dist/cli/chunk-rekqz4qg.js.map +10 -0
- package/dist/cli/{chunk-00gs3wqs.js → chunk-ta27feev.js} +1 -1
- package/dist/cli/{chunk-v6ya5kcb.js → chunk-xkz5xbtv.js} +4031 -1132
- package/dist/cli/chunk-xkz5xbtv.js.map +213 -0
- package/dist/cli/{chunk-bbnwccaz.js → chunk-y4ykbark.js} +2 -2
- package/dist/cli/{chunk-n1yg3tj3.js → chunk-y5h4r2y0.js} +6 -6
- package/dist/cli/{chunk-n1yg3tj3.js.map → chunk-y5h4r2y0.js.map} +3 -3
- package/dist/cli/{chunk-nfcyttvj.js → chunk-yj17vs39.js} +9 -9
- package/dist/cli/{chunk-nfcyttvj.js.map → chunk-yj17vs39.js.map} +1 -1
- package/dist/cli/{chunk-7vtckvaw.js → chunk-yxmk4pec.js} +16 -14
- package/dist/cli/{chunk-7vtckvaw.js.map → chunk-yxmk4pec.js.map} +2 -2
- package/dist/cli/{chunk-gs7r695n.js → chunk-zccbjcj0.js} +22 -8
- package/dist/cli/{chunk-gs7r695n.js.map → chunk-zccbjcj0.js.map} +3 -3
- package/dist/cli/{chunk-xh43dwgw.js → chunk-zf748avv.js} +415 -225
- package/dist/cli/chunk-zf748avv.js.map +37 -0
- package/dist/cli/{chunk-d1v5rhy0.js → chunk-zhvd6adm.js} +16 -14
- package/dist/cli/{chunk-d1v5rhy0.js.map → chunk-zhvd6adm.js.map} +2 -2
- package/dist/cli/index.js +28 -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 +22 -0
- package/dist/types/ai/ask.d.ts +271 -58
- package/dist/types/ai/index.d.ts +2 -2
- package/dist/types/ai/link-headers.d.ts +8 -1
- package/dist/types/ai/openapi-components.d.ts +5 -2
- package/dist/types/ai/relative-links.d.ts +9 -7
- package/dist/types/ai/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +20 -1
- package/dist/types/ai/tar.d.ts +1 -3
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/one-dollar-stats.d.ts +48 -0
- package/dist/types/analytics/schema.d.ts +14 -0
- package/dist/types/astro/integration.d.ts +11 -2
- 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/components/content/api-field.d.ts +59 -0
- package/dist/types/components/content/api-page.d.ts +95 -0
- package/dist/types/components/layout/nav-utils.d.ts +26 -0
- package/dist/types/components/openapi/helpers.d.ts +137 -0
- package/dist/types/components/openapi/security.d.ts +85 -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/base-path.d.ts +21 -7
- package/dist/types/core/config-input.d.ts +196 -27
- package/dist/types/core/data.d.ts +49 -4
- package/dist/types/core/directive-diagnostics.d.ts +12 -0
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/heading-markers.d.ts +5 -7
- package/dist/types/core/i18n-ui.d.ts +86 -0
- package/dist/types/core/i18n.d.ts +2 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/last-modified.d.ts +10 -0
- package/dist/types/core/localizable.d.ts +7 -0
- package/dist/types/core/meta.d.ts +8 -0
- package/dist/types/core/page-modes.d.ts +29 -0
- package/dist/types/core/redirect-patterns.d.ts +144 -0
- package/dist/types/core/related.d.ts +40 -0
- package/dist/types/core/schema.d.ts +558 -56
- package/dist/types/core/sources/lower.d.ts +29 -16
- package/dist/types/core/sources/normalize.d.ts +13 -1
- package/dist/types/core/sources/read.d.ts +5 -0
- package/dist/types/core/sources/watch.d.ts +12 -5
- package/dist/types/core/standard-schema.d.ts +5 -0
- package/dist/types/core/types.d.ts +1 -1
- package/dist/types/core/variables.d.ts +42 -0
- package/dist/types/deploy/artifacts.d.ts +6 -4
- package/dist/types/deploy/headers.d.ts +5 -0
- package/dist/types/deploy/node-headers.d.ts +13 -2
- package/dist/types/deploy/platforms/cloudflare.d.ts +5 -0
- package/dist/types/deploy/platforms/netlify.d.ts +7 -0
- package/dist/types/deploy/platforms/types.d.ts +8 -0
- package/dist/types/deploy/platforms/vercel.d.ts +6 -0
- package/dist/types/deploy/redirects.d.ts +61 -22
- package/dist/types/deploy/vercel-negotiation.d.ts +9 -0
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/directives.d.ts +62 -0
- package/dist/types/markdown/features.d.ts +24 -2
- package/dist/types/markdown/mdast.d.ts +63 -0
- 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/asyncapi.d.ts +4 -2
- package/dist/types/openapi/model.d.ts +12 -6
- package/dist/types/openapi/overlay.d.ts +54 -0
- package/dist/types/openapi/parse.d.ts +13 -0
- package/dist/types/openapi/references.d.ts +14 -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 +111 -0
- package/dist/types/ratelimit/upstash.d.ts +21 -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 +13 -10
- package/dist/types/reference/openapi.d.ts +33 -12
- package/dist/types/reference/options.d.ts +45 -5
- package/dist/types/reference/scalar.d.ts +13 -0
- package/dist/types/reference/schema.d.ts +60 -36
- package/dist/types/search/documents.d.ts +12 -1
- package/dist/types/search/orama-index.d.ts +4 -0
- package/dist/types/seo/metatags.d.ts +20 -0
- package/docs/02-deployment.mdx +44 -6
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/08-faq.mdx +1 -1
- package/docs/advanced/changelog.mdx +2 -2
- package/docs/advanced/custom-pages.mdx +3 -3
- package/docs/advanced/skills.mdx +14 -0
- package/docs/cli/audit.mdx +38 -2
- package/docs/cli/evals.mdx +3 -3
- package/docs/cli/index.mdx +1 -0
- package/docs/cli/translate.mdx +3 -3
- package/docs/cli/version.mdx +1 -1
- package/docs/configuration/analytics.mdx +22 -1
- package/docs/configuration/assistant.mdx +188 -36
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +6 -4
- package/docs/configuration/index.mdx +63 -5
- package/docs/configuration/meta.ts +3 -0
- package/docs/configuration/narration.mdx +119 -0
- package/docs/configuration/rate-limiting.mdx +69 -0
- package/docs/configuration/search.mdx +30 -2
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +107 -1
- package/docs/content/frontmatter.mdx +77 -1
- package/docs/content/i18n.mdx +40 -3
- package/docs/content/includes.mdx +16 -0
- package/docs/content/index.mdx +4 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/content/meta.mdx +4 -1
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +28 -6
- package/docs/content/sources/contentful.mdx +32 -0
- package/docs/content/sources/custom.mdx +39 -0
- package/docs/content/sources/github-releases.mdx +31 -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 +30 -0
- package/docs/content/sources/remote-mdx.mdx +40 -0
- package/docs/content/sources/sanity.mdx +29 -0
- package/docs/content/sources/strapi.mdx +31 -0
- package/docs/content/syntax.mdx +81 -2
- package/docs/content/variables.mdx +41 -0
- package/docs/content/versioning.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +30 -3
- package/docs/discoverability/index.mdx +3 -2
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/discoverability/open-graph.mdx +5 -3
- package/docs/discoverability/rss.mdx +1 -1
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +2 -2
- package/docs/references/graphql.mdx +3 -3
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +106 -5
- package/docs/references/scalar.mdx +3 -1
- package/package.json +36 -1
- package/skills/blume/SKILL.md +3 -0
- package/skills/blume-migrate/SKILL.md +5 -5
- package/skills/blume-migrate/references/docusaurus.md +3 -3
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +3 -3
- package/skills/blume-migrate/references/starlight.md +2 -1
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +67 -16
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +4 -3
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +12 -5
- package/src/ai/api/handlers.ts +2 -2
- package/src/ai/api/spec.ts +2 -2
- package/src/ai/api-catalog.ts +6 -2
- package/src/ai/ask-context.ts +73 -20
- package/src/ai/ask-data.ts +6 -0
- package/src/ai/ask-tools.ts +142 -0
- package/src/ai/ask.ts +354 -76
- package/src/ai/changelog-markdown.ts +2 -2
- package/src/ai/component-markdown.ts +75 -0
- package/src/ai/cors.ts +2 -0
- package/src/ai/index.ts +12 -0
- package/src/ai/link-headers.ts +14 -5
- package/src/ai/llms.ts +24 -9
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/mcp/discovery.ts +2 -2
- package/src/ai/mcp/query.ts +2 -2
- package/src/ai/mcp/server.ts +2 -2
- package/src/ai/openapi-components.ts +31 -9
- package/src/ai/relative-links.ts +42 -26
- package/src/ai/serializers.ts +2 -1
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +49 -3
- package/src/ai/tar.ts +139 -12
- package/src/ai/write-skill.ts +97 -0
- package/src/analytics/head.ts +4 -0
- package/src/analytics/index.ts +5 -0
- package/src/analytics/one-dollar-stats.ts +86 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +133 -19
- package/src/astro/include-hmr.ts +31 -12
- package/src/astro/include-refresh.ts +19 -8
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +143 -14
- package/src/astro/module-types.ts +6 -0
- package/src/astro/runtime-modules.ts +28 -13
- package/src/astro/templates.ts +364 -67
- package/src/astro/variables.ts +31 -0
- package/src/audit/catalog.ts +19 -0
- 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 +13 -13
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/graph.ts +3 -1
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +14 -9
- package/src/audit/snapshot.ts +5 -0
- package/src/audit/types.ts +2 -1
- package/src/blume-modules.d.ts +7 -0
- 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 +32 -0
- package/src/cli/command-meta.ts +5 -0
- package/src/cli/commands/audit.ts +4 -1
- package/src/cli/commands/doctor.ts +25 -17
- package/src/cli/commands/eval.ts +19 -11
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/commands/translate.ts +9 -12
- package/src/cli/commands/validate.ts +3 -1
- package/src/cli/dev-lock.ts +157 -37
- package/src/cli/index.ts +5 -0
- package/src/cli/report-format.ts +11 -6
- package/src/cli/required-secrets.ts +71 -29
- package/src/components/BlumePage.astro +2 -0
- package/src/components/colors.ts +19 -0
- package/src/components/content/ApiEndpoint.astro +18 -0
- package/src/components/content/ApiField.astro +79 -0
- package/src/components/content/ApiPlayground.astro +62 -0
- package/src/components/content/ApiRail.astro +19 -0
- package/src/components/content/Badge.astro +5 -3
- package/src/components/content/Color.astro +1 -0
- package/src/components/content/Component.astro +2 -2
- 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/Tab.astro +0 -1
- package/src/components/content/Tabs.astro +4 -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 +100 -0
- package/src/components/content/api-page.ts +224 -0
- package/src/components/content/badge-color.ts +5 -2
- package/src/components/content/base-href.ts +13 -36
- 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 +125 -18
- package/src/components/islands/base-path.ts +47 -10
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +145 -40
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/islands/webmcp.ts +4 -2
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +11 -4
- 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/DiscoveryLinks.astro +9 -5
- package/src/components/layout/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +124 -82
- package/src/components/layout/LanguageSwitcher.astro +50 -17
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +35 -9
- package/src/components/layout/NavTabMenu.astro +3 -3
- package/src/components/layout/NavTree.astro +16 -6
- package/src/components/layout/PageActions.astro +4 -3
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +78 -9
- package/src/components/layout/Pagination.astro +3 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +313 -50
- package/src/components/layout/Search.astro +64 -30
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -0
- package/src/components/layout/VersionBanner.astro +2 -2
- package/src/components/layout/analytics-client.ts +12 -0
- 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/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/analytics.ts +98 -0
- package/src/components/layout/search/flexsearch.ts +17 -5
- package/src/components/layout/search/orama-cloud.ts +26 -14
- package/src/components/layout/search/types.ts +20 -0
- package/src/components/layout/search/typesense.ts +5 -1
- package/src/components/layout/toc-active.ts +41 -0
- package/src/components/layout/toc-element.ts +8 -14
- package/src/components/openapi/ApiTagOperations.astro +2 -2
- package/src/components/openapi/AsyncApiOperation.astro +7 -4
- package/src/components/openapi/GraphqlChip.astro +2 -2
- package/src/components/openapi/Operation.astro +163 -13
- package/src/components/openapi/Playground.astro +4 -4
- package/src/components/openapi/RequestBody.astro +6 -4
- package/src/components/openapi/RequestPanel.astro +63 -8
- package/src/components/openapi/Responses.astro +6 -4
- package/src/components/openapi/SchemaTable.astro +7 -0
- package/src/components/openapi/async-snippets.ts +4 -4
- package/src/components/openapi/async.ts +38 -6
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/helpers.ts +19 -9
- package/src/components/openapi/message-composer.ts +6 -1
- package/src/components/openapi/message.ts +17 -2
- package/src/components/openapi/operation-model.ts +81 -11
- package/src/components/openapi/panel.ts +4 -2
- package/src/components/openapi/param-style.ts +181 -0
- package/src/components/openapi/playground-client.ts +73 -16
- package/src/components/openapi/request.ts +190 -26
- package/src/components/openapi/schema-tree.ts +30 -24
- package/src/components/openapi/security.ts +6 -3
- package/src/components/openapi/snippets.ts +953 -13
- package/src/components/openapi/ws-client.ts +18 -2
- 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/base-path.ts +62 -16
- package/src/core/builtin-tags.ts +5 -0
- package/src/core/client-address.ts +19 -0
- package/src/core/config-input.ts +203 -27
- package/src/core/data.ts +44 -4
- package/src/core/diagnostics.ts +5 -2
- package/src/core/directive-diagnostics.ts +99 -0
- package/src/core/footer.ts +26 -0
- package/src/core/frontmatter.ts +21 -18
- package/src/core/graph.ts +39 -36
- package/src/core/heading-markers.ts +5 -18
- package/src/core/i18n-ui.ts +82 -2
- package/src/core/i18n.ts +31 -3
- package/src/core/includes.ts +20 -3
- package/src/core/last-modified.ts +25 -3
- package/src/core/links.ts +16 -2
- package/src/core/locale-links.ts +5 -1
- package/src/core/localizable.ts +26 -0
- package/src/core/meta.ts +33 -19
- package/src/core/navigation.ts +71 -20
- package/src/core/page-modes.ts +56 -0
- package/src/core/project-graph.ts +138 -10
- package/src/core/redirect-patterns.ts +491 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +342 -68
- package/src/core/sources/contentful-rich-text.ts +27 -20
- package/src/core/sources/filesystem.ts +20 -2
- package/src/core/sources/github-releases.ts +25 -6
- package/src/core/sources/lexical.ts +23 -18
- package/src/core/sources/lower.ts +201 -34
- package/src/core/sources/mdx-remote.ts +51 -16
- package/src/core/sources/normalize.ts +327 -91
- package/src/core/sources/notion.ts +52 -25
- package/src/core/sources/obsidian.ts +23 -6
- package/src/core/sources/portable-text.ts +38 -21
- package/src/core/sources/read.ts +8 -3
- package/src/core/sources/strapi-blocks.ts +20 -16
- package/src/core/sources/watch.ts +20 -7
- package/src/core/standard-schema.ts +10 -6
- package/src/core/types.ts +1 -1
- package/src/core/ui-packs/ar.ts +43 -0
- package/src/core/ui-packs/bg.ts +45 -0
- package/src/core/ui-packs/bn.ts +43 -0
- package/src/core/ui-packs/ca.ts +46 -0
- package/src/core/ui-packs/cs.ts +44 -0
- package/src/core/ui-packs/da.ts +45 -0
- package/src/core/ui-packs/de.ts +46 -0
- package/src/core/ui-packs/el.ts +45 -0
- package/src/core/ui-packs/es.ts +46 -0
- package/src/core/ui-packs/fa.ts +45 -0
- package/src/core/ui-packs/fi.ts +45 -0
- package/src/core/ui-packs/fr.ts +46 -0
- package/src/core/ui-packs/he.ts +43 -0
- package/src/core/ui-packs/hi.ts +44 -0
- package/src/core/ui-packs/hr.ts +45 -0
- package/src/core/ui-packs/hu.ts +44 -0
- package/src/core/ui-packs/id.ts +45 -0
- package/src/core/ui-packs/it.ts +45 -0
- package/src/core/ui-packs/ja.ts +45 -0
- package/src/core/ui-packs/ko.ts +44 -0
- package/src/core/ui-packs/nl.ts +46 -0
- package/src/core/ui-packs/no.ts +44 -0
- package/src/core/ui-packs/pl.ts +45 -0
- package/src/core/ui-packs/pt-br.ts +46 -0
- package/src/core/ui-packs/pt.ts +45 -0
- package/src/core/ui-packs/ro.ts +45 -0
- package/src/core/ui-packs/ru.ts +45 -0
- package/src/core/ui-packs/sk.ts +44 -0
- package/src/core/ui-packs/sr.ts +45 -0
- package/src/core/ui-packs/sv.ts +44 -0
- package/src/core/ui-packs/th.ts +43 -0
- package/src/core/ui-packs/tr.ts +45 -0
- package/src/core/ui-packs/uk.ts +45 -0
- package/src/core/ui-packs/vi.ts +45 -0
- package/src/core/ui-packs/zh-tw.ts +43 -0
- package/src/core/ui-packs/zh.ts +43 -0
- package/src/core/variables.ts +103 -0
- package/src/core/version-cut.ts +52 -15
- package/src/deploy/artifacts.ts +104 -14
- package/src/deploy/cloudflare-negotiation.ts +39 -17
- package/src/deploy/headers.ts +10 -5
- package/src/deploy/node-headers.ts +64 -10
- package/src/deploy/platforms/cloudflare.ts +44 -3
- package/src/deploy/platforms/netlify.ts +34 -1
- package/src/deploy/platforms/node.ts +1 -0
- package/src/deploy/platforms/static.ts +1 -0
- package/src/deploy/platforms/types.ts +8 -0
- package/src/deploy/platforms/vercel.ts +38 -2
- package/src/deploy/redirects.ts +139 -36
- package/src/deploy/robots.ts +2 -2
- package/src/deploy/rss.ts +4 -3
- package/src/deploy/sitemap.ts +7 -5
- package/src/deploy/vercel-negotiation.ts +36 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/base-links.ts +34 -36
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/directives.ts +242 -36
- package/src/markdown/features.ts +20 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +35 -11
- package/src/markdown/math.ts +4 -4
- package/src/markdown/mdast.ts +5 -2
- package/src/markdown/relative-links.ts +12 -4
- 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 +136 -11
- package/src/og/derive.ts +41 -31
- package/src/og/index.ts +1 -0
- package/src/openapi/asyncapi.ts +4 -2
- package/src/openapi/model.ts +77 -36
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +55 -2
- package/src/openapi/proxy.ts +63 -10
- package/src/openapi/references.ts +21 -5
- package/src/openapi/render-mdx.ts +46 -3
- package/src/openapi/scalar.ts +40 -6
- package/src/openapi/source.ts +1 -1
- 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 +229 -0
- package/src/ratelimit/schema.ts +28 -0
- package/src/ratelimit/upstash.ts +30 -0
- package/src/ratelimit/wrangler.ts +70 -0
- package/src/reference/asyncapi.ts +2 -2
- package/src/reference/graphql.ts +5 -2
- package/src/reference/openapi.ts +21 -7
- package/src/reference/options.ts +51 -7
- package/src/reference/scalar.ts +33 -8
- package/src/registry/eject.ts +201 -28
- package/src/registry/registry.ts +7 -0
- package/src/search/adapters/version-scope.ts +30 -0
- package/src/search/documents.ts +93 -28
- package/src/search/orama-index.ts +28 -3
- package/src/search/pagefind-ranking.ts +39 -0
- package/src/search/popular.ts +2 -1
- package/src/search/sync/algolia.ts +15 -4
- package/src/search/sync/orama-cloud.ts +3 -0
- package/src/search/sync/typesense.ts +5 -0
- package/src/seo/jsonld.ts +7 -3
- package/src/seo/metatags.ts +67 -0
- package/src/theme/chrome-icons.ts +1 -0
- package/src/theme/color.ts +500 -0
- package/src/theme/entry.ts +56 -3
- package/src/theme/palette.ts +121 -45
- package/src/translate/meta.ts +68 -24
- package/src/translate/prompts.ts +1 -0
- package/src/translate/run.ts +3 -3
- package/src/translate/validate.ts +12 -3
- package/src/translate/work-list.ts +56 -17
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-3yce002v.js.map +0 -14
- package/dist/cli/chunk-5r8g91qn.js.map +0 -34
- package/dist/cli/chunk-6dtt0zfn.js.map +0 -10
- package/dist/cli/chunk-7mbqtmgb.js.map +0 -10
- package/dist/cli/chunk-ddndchfr.js.map +0 -14
- package/dist/cli/chunk-esh98wmb.js +0 -23
- package/dist/cli/chunk-esh98wmb.js.map +0 -10
- package/dist/cli/chunk-hqp2ajnh.js.map +0 -35
- package/dist/cli/chunk-hr8ne106.js.map +0 -13
- package/dist/cli/chunk-j85scx15.js.map +0 -10
- package/dist/cli/chunk-mqc662a6.js +0 -75
- package/dist/cli/chunk-mqc662a6.js.map +0 -11
- package/dist/cli/chunk-pbg5a4s3.js.map +0 -19
- package/dist/cli/chunk-qkb5a8sa.js.map +0 -10
- package/dist/cli/chunk-v6ya5kcb.js.map +0 -189
- package/dist/cli/chunk-wdrt2k2v.js.map +0 -10
- package/dist/cli/chunk-xh43dwgw.js.map +0 -36
- package/docs/content/sources.mdx +0 -338
- /package/dist/cli/{chunk-zp79m0ts.js.map → chunk-1n228xqb.js.map} +0 -0
- /package/dist/cli/{chunk-h2ez8dzb.js.map → chunk-5ffywsmb.js.map} +0 -0
- /package/dist/cli/{chunk-g698a744.js.map → chunk-5wj2qwn2.js.map} +0 -0
- /package/dist/cli/{chunk-sqw4ekg1.js.map → chunk-amrb6hgh.js.map} +0 -0
- /package/dist/cli/{chunk-h7k3nq3v.js.map → chunk-gp84za0h.js.map} +0 -0
- /package/dist/cli/{chunk-bfwp9vp6.js.map → chunk-p804f3pe.js.map} +0 -0
- /package/dist/cli/{chunk-00gs3wqs.js.map → chunk-ta27feev.js.map} +0 -0
- /package/dist/cli/{chunk-bbnwccaz.js.map → chunk-y4ykbark.js.map} +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
+
import { captchaToken } from "../../captcha/client.ts";
|
|
4
|
+
import type { CaptchaSettings } from "../../captcha/schema.ts";
|
|
3
5
|
import type { BlumeClientData } from "../../core/data.ts";
|
|
4
6
|
import { track } from "../layout/analytics-client.ts";
|
|
5
7
|
import type { SearchFn, SearchResult } from "../layout/search/types.ts";
|
|
6
8
|
import { joinBase, stripBase } from "./base-path.ts";
|
|
9
|
+
import { newThreadId } from "./support-link.ts";
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* React hooks for Blume islands.
|
|
@@ -23,27 +26,29 @@ import { joinBase, stripBase } from "./base-path.ts";
|
|
|
23
26
|
|
|
24
27
|
export type { BlumeClientData } from "../../core/data.ts";
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
/**
|
|
30
|
+
* The last parsed snapshot, keyed by the script text it came from. Each page
|
|
31
|
+
* renders its own snapshot (its route and title, its locale's navigation), and
|
|
32
|
+
* a client-router navigation swaps in the new page's tag, so the cache is only
|
|
33
|
+
* reused while the tag's text is unchanged.
|
|
34
|
+
*/
|
|
35
|
+
let cached: { data: BlumeClientData; text: string } | null = null;
|
|
27
36
|
|
|
28
|
-
/** Read + parse the injected snapshot (memoized
|
|
37
|
+
/** Read + parse the current page's injected snapshot (memoized per text). */
|
|
29
38
|
const readClientData = (): BlumeClientData | null => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
// `document` is undeclared on the server; probing `globalThis` avoids both
|
|
34
|
-
// the bare-reference ReferenceError and a `typeof` sniff.
|
|
35
|
-
if (!("document" in globalThis)) {
|
|
39
|
+
const text = document.querySelector("#blume-client-data")?.textContent;
|
|
40
|
+
if (!text) {
|
|
36
41
|
return null;
|
|
37
42
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return null;
|
|
43
|
+
if (cached?.text === text) {
|
|
44
|
+
return cached.data;
|
|
41
45
|
}
|
|
42
46
|
try {
|
|
43
47
|
// SAFETY: the layout serialized this script tag's JSON from the same
|
|
44
48
|
// `BlumeClientData` snapshot this reads back.
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
const data = JSON.parse(text) as BlumeClientData;
|
|
50
|
+
cached = { data, text };
|
|
51
|
+
return data;
|
|
47
52
|
} catch {
|
|
48
53
|
return null;
|
|
49
54
|
}
|
|
@@ -51,15 +56,29 @@ const readClientData = (): BlumeClientData | null => {
|
|
|
51
56
|
|
|
52
57
|
/**
|
|
53
58
|
* Read the injected snapshot after mount. `null` on the server and on the first
|
|
54
|
-
* client render (so hydration matches), then the data once mounted
|
|
59
|
+
* client render (so hydration matches), then the data once mounted, re-read
|
|
60
|
+
* after every client-router swap.
|
|
55
61
|
*/
|
|
56
62
|
const useClientData = (): BlumeClientData | null => {
|
|
57
63
|
const [data, setData] = useState<BlumeClientData | null>(null);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
// `document` is undeclared on the server; probing `globalThis` avoids both
|
|
66
|
+
// the bare-reference ReferenceError and a `typeof` sniff.
|
|
67
|
+
if (!("document" in globalThis)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const sync = () => setData(readClientData());
|
|
71
|
+
// Intentional post-mount hydration guard: `null` on the server and first
|
|
72
|
+
// client render so hydration matches, then the snapshot once mounted. The
|
|
73
|
+
// extra render is required; do not seed the initial value from the DOM.
|
|
74
|
+
// oxlint-disable-next-line react/react-compiler, react/set-state-in-effect, react-doctor/no-initialize-state -- deliberate SSR hydration guard
|
|
75
|
+
sync();
|
|
76
|
+
// An island kept across navigations (`transition:persist`) outlives the
|
|
77
|
+
// page it mounted on; the swap brings the next page's snapshot, so read
|
|
78
|
+
// it again or the hook keeps answering with the first page's data.
|
|
79
|
+
document.addEventListener("astro:after-swap", sync);
|
|
80
|
+
return () => document.removeEventListener("astro:after-swap", sync);
|
|
81
|
+
}, []);
|
|
63
82
|
return data;
|
|
64
83
|
};
|
|
65
84
|
|
|
@@ -143,11 +162,22 @@ export interface UseAssistant {
|
|
|
143
162
|
loading: boolean;
|
|
144
163
|
messages: AskMessage[];
|
|
145
164
|
reset: () => void;
|
|
165
|
+
/**
|
|
166
|
+
* The conversation's id, new after every `reset()`. The `ask` analytics
|
|
167
|
+
* events carry it as `thread`, and the support link passes it along.
|
|
168
|
+
*/
|
|
169
|
+
thread: string;
|
|
146
170
|
}
|
|
147
171
|
|
|
148
172
|
const DEFAULT_ASK_ENDPOINT = joinBase(import.meta.env.BASE_URL, "api/ask");
|
|
149
173
|
|
|
150
174
|
export interface UseAssistantOptions {
|
|
175
|
+
/**
|
|
176
|
+
* The site's bot check (`ai.assistant.captcha`), as `useBlume().config
|
|
177
|
+
* .assistant.captcha` holds it: each question gets a fresh token, sent as
|
|
178
|
+
* `captcha` in the request body.
|
|
179
|
+
*/
|
|
180
|
+
captcha?: CaptchaSettings | null;
|
|
151
181
|
/** Existing assistant endpoint; defaults to Blume's generated `/api/ask`. */
|
|
152
182
|
endpoint?: string;
|
|
153
183
|
/**
|
|
@@ -156,11 +186,38 @@ export interface UseAssistantOptions {
|
|
|
156
186
|
* dictionary string.
|
|
157
187
|
*/
|
|
158
188
|
errorMessage?: string;
|
|
189
|
+
/**
|
|
190
|
+
* Shown as the assistant's answer when the route's rate limit turns the
|
|
191
|
+
* question away (`429`). Defaults to an English notice; the built-in
|
|
192
|
+
* island passes its localized dictionary string.
|
|
193
|
+
*/
|
|
194
|
+
rateLimitMessage?: string;
|
|
195
|
+
/**
|
|
196
|
+
* Shown as the assistant's answer when the bot check fails, in the browser
|
|
197
|
+
* or at the route (`403`). Defaults to an English notice; the built-in
|
|
198
|
+
* island passes its localized dictionary string.
|
|
199
|
+
*/
|
|
200
|
+
verifyMessage?: string;
|
|
159
201
|
}
|
|
160
202
|
|
|
161
203
|
/** Shown as the assistant's answer when the request fails or throws. */
|
|
162
204
|
const ASK_ERROR = "Something went wrong answering that. Please try again.";
|
|
163
205
|
|
|
206
|
+
/** Shown as the assistant's answer when the rate limit turns a question away. */
|
|
207
|
+
const ASK_RATE_LIMITED =
|
|
208
|
+
"You've asked a lot of questions. Try again in a few minutes.";
|
|
209
|
+
|
|
210
|
+
/** Shown as the assistant's answer when the bot check fails. */
|
|
211
|
+
const ASK_VERIFY_FAILED = "We couldn't check that you're human. Try again.";
|
|
212
|
+
|
|
213
|
+
/** What the assistant sends the route. */
|
|
214
|
+
interface AskPayload {
|
|
215
|
+
/** The bot check's token, when the site has one. */
|
|
216
|
+
captcha?: string;
|
|
217
|
+
messages: AskMessage[];
|
|
218
|
+
page: { path: string };
|
|
219
|
+
}
|
|
220
|
+
|
|
164
221
|
/** How the assistant's generated route opens its missing-credential notice. */
|
|
165
222
|
const NOT_CONFIGURED = /^The assistant is not configured/u;
|
|
166
223
|
|
|
@@ -183,6 +240,26 @@ const unavailableNotice = async (
|
|
|
183
240
|
return NOT_CONFIGURED.test(text) && text.length <= 200 ? text : null;
|
|
184
241
|
};
|
|
185
242
|
|
|
243
|
+
/** The notices the route's refusals map to. */
|
|
244
|
+
interface RefusalNotices {
|
|
245
|
+
rateLimitMessage: string;
|
|
246
|
+
verifyMessage: string;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The reader-facing notice for the route turning a question away: the rate
|
|
251
|
+
* limit (`429`) or the bot check (`403`), or `null` for any other status.
|
|
252
|
+
*/
|
|
253
|
+
const refusalNotice = (
|
|
254
|
+
status: number,
|
|
255
|
+
notices: RefusalNotices
|
|
256
|
+
): string | null => {
|
|
257
|
+
if (status === 429) {
|
|
258
|
+
return notices.rateLimitMessage;
|
|
259
|
+
}
|
|
260
|
+
return status === 403 ? notices.verifyMessage : null;
|
|
261
|
+
};
|
|
262
|
+
|
|
186
263
|
/** The current route with the deployment base stripped, for page grounding. */
|
|
187
264
|
const currentPath = (): string =>
|
|
188
265
|
stripBase(import.meta.env.BASE_URL, window.location.pathname);
|
|
@@ -196,8 +273,12 @@ export const useAssistant = (
|
|
|
196
273
|
): UseAssistant => {
|
|
197
274
|
const endpoint = options.endpoint ?? DEFAULT_ASK_ENDPOINT;
|
|
198
275
|
const errorMessage = options.errorMessage ?? ASK_ERROR;
|
|
276
|
+
const rateLimitMessage = options.rateLimitMessage ?? ASK_RATE_LIMITED;
|
|
277
|
+
const verifyMessage = options.verifyMessage ?? ASK_VERIFY_FAILED;
|
|
278
|
+
const { captcha } = options;
|
|
199
279
|
const [messages, setMessages] = useState<AskMessage[]>([]);
|
|
200
280
|
const [loading, setLoading] = useState(false);
|
|
281
|
+
const [thread, setThread] = useState(newThreadId);
|
|
201
282
|
// The stream writes into the conversation via state updates, so `reset()`
|
|
202
283
|
// mid-answer must revoke the in-flight request's right to write — otherwise
|
|
203
284
|
// its next chunk re-appends the assistant bubble onto the emptied list, and
|
|
@@ -239,7 +320,7 @@ export const useAssistant = (
|
|
|
239
320
|
) =>
|
|
240
321
|
track(
|
|
241
322
|
event,
|
|
242
|
-
{ ...props, path: pathname, questionChars: trimmed.length },
|
|
323
|
+
{ ...props, path: pathname, questionChars: trimmed.length, thread },
|
|
243
324
|
{ question: trimmed }
|
|
244
325
|
);
|
|
245
326
|
report("ask", {});
|
|
@@ -264,12 +345,32 @@ export const useAssistant = (
|
|
|
264
345
|
const assistant: AskMessage = { content: "", role: "assistant" };
|
|
265
346
|
setMessages([...history, assistant]);
|
|
266
347
|
setLoading(true);
|
|
348
|
+
// Answer with a notice instead: an error, or the route turning the
|
|
349
|
+
// question away. Silent once a reset has revoked the question.
|
|
350
|
+
const fail = (notice: string) => {
|
|
351
|
+
if (live()) {
|
|
352
|
+
outcome("ask_error", { status });
|
|
353
|
+
assistant.content = notice;
|
|
354
|
+
setMessages([...history, { ...assistant }]);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
267
357
|
try {
|
|
358
|
+
// A fresh bot-check token per question, when the site has a check;
|
|
359
|
+
// `null` when the check didn't pass in the browser.
|
|
360
|
+
const token = captcha
|
|
361
|
+
? await captchaToken(captcha).catch(() => null)
|
|
362
|
+
: undefined;
|
|
363
|
+
if (token === null) {
|
|
364
|
+
fail(verifyMessage);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const payload: AskPayload = {
|
|
368
|
+
captcha: token,
|
|
369
|
+
messages: history,
|
|
370
|
+
page: { path },
|
|
371
|
+
};
|
|
268
372
|
const response = await fetch(endpoint, {
|
|
269
|
-
body: JSON.stringify(
|
|
270
|
-
messages: history,
|
|
271
|
-
page: { path },
|
|
272
|
-
}),
|
|
373
|
+
body: JSON.stringify(payload),
|
|
273
374
|
headers: { "content-type": "application/json" },
|
|
274
375
|
method: "POST",
|
|
275
376
|
signal: controller.signal,
|
|
@@ -278,12 +379,12 @@ export const useAssistant = (
|
|
|
278
379
|
if (!response.ok) {
|
|
279
380
|
// An error body (JSON, HTML error page) must not stream in as the
|
|
280
381
|
// assistant's answer — only the route's own not-configured notice.
|
|
281
|
-
const notice =
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
382
|
+
const notice =
|
|
383
|
+
refusalNotice(status, {
|
|
384
|
+
rateLimitMessage,
|
|
385
|
+
verifyMessage,
|
|
386
|
+
}) ?? (await unavailableNotice(response).catch(() => null));
|
|
387
|
+
fail(notice ?? errorMessage);
|
|
287
388
|
return;
|
|
288
389
|
}
|
|
289
390
|
const reader = response.body?.getReader();
|
|
@@ -316,20 +417,14 @@ export const useAssistant = (
|
|
|
316
417
|
if (assistant.content) {
|
|
317
418
|
outcome("ask_answer", { chars: assistant.content.length });
|
|
318
419
|
} else {
|
|
319
|
-
|
|
320
|
-
assistant.content = errorMessage;
|
|
321
|
-
setMessages([...history, { ...assistant }]);
|
|
420
|
+
fail(errorMessage);
|
|
322
421
|
}
|
|
323
422
|
}
|
|
324
423
|
} catch {
|
|
325
424
|
// A thrown fetch (offline, DNS failure, CORS) must not strand the
|
|
326
425
|
// pre-appended empty assistant message as a stuck placeholder. A
|
|
327
426
|
// reset's abort lands here too — the guard keeps it silent.
|
|
328
|
-
|
|
329
|
-
outcome("ask_error", { status });
|
|
330
|
-
assistant.content = errorMessage;
|
|
331
|
-
setMessages([...history, { ...assistant }]);
|
|
332
|
-
}
|
|
427
|
+
fail(errorMessage);
|
|
333
428
|
// oxlint-disable-next-line react/todo -- React Compiler cannot lower try/finally; the hook stays manually memoized above
|
|
334
429
|
} finally {
|
|
335
430
|
if (live()) {
|
|
@@ -337,7 +432,16 @@ export const useAssistant = (
|
|
|
337
432
|
}
|
|
338
433
|
}
|
|
339
434
|
},
|
|
340
|
-
[
|
|
435
|
+
[
|
|
436
|
+
captcha,
|
|
437
|
+
endpoint,
|
|
438
|
+
errorMessage,
|
|
439
|
+
loading,
|
|
440
|
+
messages,
|
|
441
|
+
rateLimitMessage,
|
|
442
|
+
thread,
|
|
443
|
+
verifyMessage,
|
|
444
|
+
]
|
|
341
445
|
);
|
|
342
446
|
|
|
343
447
|
// Retained for the compiler-off opt-out path (`react: { compiler: false }`):
|
|
@@ -348,9 +452,10 @@ export const useAssistant = (
|
|
|
348
452
|
abortRef.current?.abort();
|
|
349
453
|
abortRef.current = null;
|
|
350
454
|
setMessages([]);
|
|
455
|
+
setThread(newThreadId());
|
|
351
456
|
// The in-flight `ask`'s finally is now stale and won't clear this.
|
|
352
457
|
setLoading(false);
|
|
353
458
|
}, []);
|
|
354
459
|
|
|
355
|
-
return { ask, loading, messages, reset };
|
|
460
|
+
return { ask, loading, messages, reset, thread };
|
|
356
461
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The assistant's handoff to your support channel (`ai.assistant.support`).
|
|
3
|
+
* A `mailto:` link starts an email with the conversation as its body, so the
|
|
4
|
+
* reader doesn't have to repeat themselves; any other link gets the
|
|
5
|
+
* conversation's `thread` id in its query, the same id the assistant's
|
|
6
|
+
* analytics events carry, so a support team can find the conversation.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** One turn of the conversation. */
|
|
10
|
+
export interface SupportMessage {
|
|
11
|
+
content: string;
|
|
12
|
+
role: "assistant" | "user";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** What the link is built from. */
|
|
16
|
+
export interface SupportContext {
|
|
17
|
+
/** The label the transcript gives the assistant's turns. */
|
|
18
|
+
ai: string;
|
|
19
|
+
messages: readonly SupportMessage[];
|
|
20
|
+
/** The email's subject. */
|
|
21
|
+
subject: string;
|
|
22
|
+
thread: string;
|
|
23
|
+
/** The label the transcript gives the reader's turns. */
|
|
24
|
+
you: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* How much conversation an email carries: mail clients cut long `mailto:`
|
|
29
|
+
* links, so a long conversation is trimmed from the start, keeping the
|
|
30
|
+
* latest turns.
|
|
31
|
+
*/
|
|
32
|
+
export const MAX_TRANSCRIPT_CHARS = 1500;
|
|
33
|
+
|
|
34
|
+
const transcriptOf = (context: SupportContext): string => {
|
|
35
|
+
const full = context.messages
|
|
36
|
+
.map(
|
|
37
|
+
(message) =>
|
|
38
|
+
`${message.role === "user" ? context.you : context.ai}: ${message.content}`
|
|
39
|
+
)
|
|
40
|
+
.join("\n\n");
|
|
41
|
+
return full.length > MAX_TRANSCRIPT_CHARS
|
|
42
|
+
? `…${full.slice(-MAX_TRANSCRIPT_CHARS)}`
|
|
43
|
+
: full;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** The support link for this conversation. */
|
|
47
|
+
export const supportHref = (
|
|
48
|
+
support: string,
|
|
49
|
+
context: SupportContext
|
|
50
|
+
): string => {
|
|
51
|
+
if (support.startsWith("mailto:")) {
|
|
52
|
+
// `mailto:` wants %20, not the `+` URLSearchParams writes for a space.
|
|
53
|
+
const body = `${transcriptOf(context)}\n\n(${context.thread})`;
|
|
54
|
+
const separator = support.includes("?") ? "&" : "?";
|
|
55
|
+
return `${support}${separator}subject=${encodeURIComponent(context.subject)}&body=${encodeURIComponent(body)}`;
|
|
56
|
+
}
|
|
57
|
+
const url = new URL(support, "https://blume.invalid");
|
|
58
|
+
url.searchParams.set("thread", context.thread);
|
|
59
|
+
return url.origin === "https://blume.invalid"
|
|
60
|
+
? `${url.pathname}${url.search}${url.hash}`
|
|
61
|
+
: url.href;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** A fresh conversation id: 16 hex characters. */
|
|
65
|
+
export const newThreadId = (): string =>
|
|
66
|
+
Array.from(crypto.getRandomValues(new Uint8Array(8)), (byte) =>
|
|
67
|
+
byte.toString(16).padStart(2, "0")
|
|
68
|
+
).join("");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { unescape } from "html-escaper";
|
|
2
2
|
|
|
3
3
|
import type { SearchFn } from "../layout/search/types.ts";
|
|
4
|
-
import { joinBase, prefixBase } from "./base-path.ts";
|
|
4
|
+
import { joinBase, mountBase, prefixBase } from "./base-path.ts";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* WebMCP (W3C Web Machine Learning CG): in-page tools an agentic browser can
|
|
@@ -144,7 +144,7 @@ export const buildWebMcpTools = (options: WebMcpToolOptions): WebMcpTool[] => {
|
|
|
144
144
|
hits
|
|
145
145
|
.map(
|
|
146
146
|
(hit) =>
|
|
147
|
-
`${plain(hit.title)} — ${
|
|
147
|
+
`${plain(hit.title)} — ${mountBase(options.base, hit.url)}\n${plain(hit.excerpt)}`
|
|
148
148
|
)
|
|
149
149
|
.join("\n\n")
|
|
150
150
|
);
|
|
@@ -175,6 +175,8 @@ export const buildWebMcpTools = (options: WebMcpToolOptions): WebMcpTool[] => {
|
|
|
175
175
|
}
|
|
176
176
|
const trimmed = route.length > 1 ? route.replace(/\/+$/u, "") : route;
|
|
177
177
|
const target = trimmed === "/" ? "/index" : trimmed;
|
|
178
|
+
// The agent may hand back a served URL `search_docs` returned, base
|
|
179
|
+
// included, so a path already under the base keeps it (`prefixBase`).
|
|
178
180
|
const response = await fetchFn(
|
|
179
181
|
`${prefixBase(options.base, target)}.md`
|
|
180
182
|
).catch(() => null);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import VercelAnalytics from "@vercel/analytics/astro";
|
|
3
|
+
import data from "blume:data";
|
|
3
4
|
|
|
4
5
|
import { analyticsHead } from "../../analytics/head.ts";
|
|
5
6
|
import type { BlumeDataConfig } from "../../core/data.ts";
|
|
@@ -10,12 +11,20 @@ import type { BlumeDataConfig } from "../../core/data.ts";
|
|
|
10
11
|
// component for `vercel()`, one <script> for every other adapter. Loads ONLY
|
|
11
12
|
// in production builds so `blume dev` stays clean and local traffic never
|
|
12
13
|
// reaches your analytics.
|
|
14
|
+
//
|
|
15
|
+
// With `consent` configured, every tag is held as inert `text/plain` until
|
|
16
|
+
// the reader allows analytics, when the consent runtime runs it
|
|
17
|
+
// (`consent/client.ts`); Vercel's component sends nothing until then instead
|
|
18
|
+
// (`consent/init.ts`).
|
|
13
19
|
interface Props {
|
|
14
20
|
analytics?: BlumeDataConfig["analytics"] | null;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
const { analytics } = Astro.props;
|
|
18
24
|
const nodes = analyticsHead(import.meta.env.PROD ? (analytics ?? []) : []);
|
|
25
|
+
const held = data.config.consent
|
|
26
|
+
? { "data-blume-consent": "analytics", type: "text/plain" }
|
|
27
|
+
: {};
|
|
19
28
|
---
|
|
20
29
|
|
|
21
30
|
{
|
|
@@ -24,9 +33,9 @@ const nodes = analyticsHead(import.meta.env.PROD ? (analytics ?? []) : []);
|
|
|
24
33
|
return <VercelAnalytics {...node.props} />;
|
|
25
34
|
}
|
|
26
35
|
return node.content === null ? (
|
|
27
|
-
<script is:inline {...node.attributes} />
|
|
36
|
+
<script is:inline {...node.attributes} {...held} />
|
|
28
37
|
) : (
|
|
29
|
-
<script is:inline set:html={node.content} {...node.attributes} />
|
|
38
|
+
<script is:inline set:html={node.content} {...node.attributes} {...held} />
|
|
30
39
|
);
|
|
31
40
|
})
|
|
32
41
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
import data from "blume:data";
|
|
3
|
+
import type { BlumeBanner } from "../../core/data.ts";
|
|
3
4
|
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
4
5
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
6
|
+
import { resolveLocalizable } from "../../core/localizable.ts";
|
|
5
7
|
import { withBase } from "../islands/base-path.ts";
|
|
6
8
|
import Icon from "../Icon.astro";
|
|
7
9
|
|
|
@@ -9,14 +11,11 @@ import Icon from "../Icon.astro";
|
|
|
9
11
|
// every shell shows the same bar. The pre-paint hide for a dismissed banner and
|
|
10
12
|
// the dismiss click handler live in the layout/Header, not here.
|
|
11
13
|
interface Props {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
key: string;
|
|
18
|
-
}
|
|
19
|
-
| null;
|
|
14
|
+
/**
|
|
15
|
+
* The banner. Its `content` and link `text` may be per-locale maps, shown
|
|
16
|
+
* in `locale` (else the default locale, else the map's first entry).
|
|
17
|
+
*/
|
|
18
|
+
banner: BlumeBanner | null;
|
|
20
19
|
/** Localized banner labels. */
|
|
21
20
|
strings?: UIStrings["banner"];
|
|
22
21
|
/**
|
|
@@ -40,6 +39,11 @@ const b = { ...EN_UI.banner, ...strings };
|
|
|
40
39
|
const navigation =
|
|
41
40
|
(locale === undefined ? undefined : data.navigationByLocale[locale]) ??
|
|
42
41
|
data.navigation;
|
|
42
|
+
const defaultLocale = data.config.i18n?.defaultLocale;
|
|
43
|
+
const content =
|
|
44
|
+
banner && resolveLocalizable(banner.content, locale, defaultLocale);
|
|
45
|
+
const linkText =
|
|
46
|
+
banner?.link && resolveLocalizable(banner.link.text, locale, defaultLocale);
|
|
43
47
|
const linkHref =
|
|
44
48
|
banner?.link && banner.link.href === data.config.banner?.link?.href
|
|
45
49
|
? (navigation.bannerHref ?? banner.link.href)
|
|
@@ -55,13 +59,13 @@ const linkHref =
|
|
|
55
59
|
>
|
|
56
60
|
{/* The message is authored in one language, whatever the page's
|
|
57
61
|
direction: `dir="auto"` keeps its punctuation where it was typed. */}
|
|
58
|
-
<span dir="auto">{
|
|
62
|
+
<span dir="auto">{content}</span>
|
|
59
63
|
{banner.link && linkHref && (
|
|
60
64
|
<a
|
|
61
65
|
class="inline-flex items-center gap-1 font-medium underline underline-offset-2"
|
|
62
66
|
href={withBase(linkHref)}
|
|
63
67
|
>
|
|
64
|
-
{
|
|
68
|
+
{linkText}
|
|
65
69
|
<Icon class="rtl:-scale-x-100" name="arrow-right" size={14} />
|
|
66
70
|
</a>
|
|
67
71
|
)}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
3
3
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { withMountedBase } from "../islands/base-path.ts";
|
|
5
5
|
import type { Crumb } from "./nav-utils.ts";
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
@@ -11,9 +11,11 @@ interface Props {
|
|
|
11
11
|
strings?: UIStrings["nav"];
|
|
12
12
|
/** Left-align full width (for the wide API layout) instead of the prose measure. */
|
|
13
13
|
wide?: boolean;
|
|
14
|
+
/** Match a `center` page's wider centered column instead of the prose measure. */
|
|
15
|
+
centered?: boolean;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
const { crumbs, strings, wide = false } = Astro.props;
|
|
18
|
+
const { crumbs, strings, wide = false, centered = false } = Astro.props;
|
|
17
19
|
|
|
18
20
|
// Merge over the English defaults so a label missing from a translation (or
|
|
19
21
|
// from a not-yet-regenerated snapshot) still renders instead of coming out
|
|
@@ -31,11 +33,16 @@ const eyebrowCrumb = crumbs.length > 1 ? crumbs[crumbs.length - 2] : null;
|
|
|
31
33
|
aria-label={n.breadcrumb}
|
|
32
34
|
class:list={[
|
|
33
35
|
"mb-2 text-muted-foreground text-sm",
|
|
34
|
-
wide
|
|
36
|
+
wide
|
|
37
|
+
? "max-w-none"
|
|
38
|
+
: ["mx-auto", centered ? "max-w-[54rem]" : "max-w-content"],
|
|
35
39
|
]}
|
|
36
40
|
>
|
|
37
41
|
{eyebrowCrumb.route ? (
|
|
38
|
-
<a
|
|
42
|
+
<a
|
|
43
|
+
class="hover:text-foreground"
|
|
44
|
+
href={withMountedBase(eyebrowCrumb.route)}
|
|
45
|
+
>
|
|
39
46
|
{eyebrowCrumb.label}
|
|
40
47
|
</a>
|
|
41
48
|
) : (
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
3
|
+
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
4
|
+
import { newTabAttrs } from "../../core/new-tab.ts";
|
|
5
|
+
import { withBase } from "../islands/base-path.ts";
|
|
6
|
+
|
|
7
|
+
// The `native()` consent banner: a card at the foot of the viewport asking to
|
|
8
|
+
// use analytics cookies. It renders hidden, and the consent runtime
|
|
9
|
+
// (`consent/client.ts`) shows it until the reader answers, stores the answer,
|
|
10
|
+
// and shows it again from the footer's Cookie settings link. Accept and
|
|
11
|
+
// Decline carry the same weight, so declining is as easy as accepting.
|
|
12
|
+
interface Props {
|
|
13
|
+
/** The privacy policy link: a page route or a URL. */
|
|
14
|
+
policy?: string;
|
|
15
|
+
/** Localized banner labels. */
|
|
16
|
+
strings?: UIStrings["consent"];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { policy, strings } = Astro.props;
|
|
20
|
+
const c = { ...EN_UI.consent, ...strings };
|
|
21
|
+
const buttonClass =
|
|
22
|
+
"flex-1 rounded-blume border border-border px-4 py-2 text-foreground text-sm transition-colors hover:border-foreground";
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<div
|
|
26
|
+
aria-label={c.label}
|
|
27
|
+
class="fixed inset-x-4 bottom-4 z-50 rounded-blume border border-border bg-background p-4 shadow-lg sm:start-auto sm:end-4 sm:w-96"
|
|
28
|
+
data-blume-consent-banner
|
|
29
|
+
hidden
|
|
30
|
+
role="region"
|
|
31
|
+
>
|
|
32
|
+
<p class="text-foreground text-sm">
|
|
33
|
+
{c.message}{" "}
|
|
34
|
+
{
|
|
35
|
+
policy && (
|
|
36
|
+
<a
|
|
37
|
+
class="text-muted-foreground underline underline-offset-2 transition-colors hover:text-foreground"
|
|
38
|
+
href={withBase(policy)}
|
|
39
|
+
{...newTabAttrs(policy)}
|
|
40
|
+
>
|
|
41
|
+
{c.policy}
|
|
42
|
+
</a>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
</p>
|
|
46
|
+
<div class="mt-3 flex gap-2">
|
|
47
|
+
<button
|
|
48
|
+
class={buttonClass}
|
|
49
|
+
data-blume-consent-choice="decline"
|
|
50
|
+
type="button"
|
|
51
|
+
>
|
|
52
|
+
{c.decline}
|
|
53
|
+
</button>
|
|
54
|
+
<button
|
|
55
|
+
class={buttonClass}
|
|
56
|
+
data-blume-consent-choice="accept"
|
|
57
|
+
type="button"
|
|
58
|
+
>
|
|
59
|
+
{c.accept}
|
|
60
|
+
</button>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { consentHead } from "../../consent/head.ts";
|
|
3
|
+
import { CONSENT_INIT_SCRIPT } from "../../consent/init.ts";
|
|
4
|
+
import type { BlumeDataConfig } from "../../core/data.ts";
|
|
5
|
+
|
|
6
|
+
// The consent layer's <head> output, rendered ahead of `Analytics.astro` by
|
|
7
|
+
// every layout when `consent` is configured: the inline script that creates
|
|
8
|
+
// `window.blumeConsent` (`consent/init.ts`), the adapter's own tags — a
|
|
9
|
+
// hosted consent manager's loader and the bridge that reports its answers —
|
|
10
|
+
// and the runtime that runs the held analytics (`consent/client.ts`), which
|
|
11
|
+
// starts the adapter's browser module, if it has one (`blume:consent-client`,
|
|
12
|
+
// see `consent/clients.ts`). Like analytics, a hosted manager loads in
|
|
13
|
+
// production builds only; a browser module (`native()`'s banner) works in
|
|
14
|
+
// `blume dev` too.
|
|
15
|
+
interface Props {
|
|
16
|
+
consent: NonNullable<BlumeDataConfig["consent"]>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { consent } = Astro.props;
|
|
20
|
+
const tags = import.meta.env.PROD ? consentHead(consent) : [];
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
<script data-kind={consent.kind} is:inline set:html={CONSENT_INIT_SCRIPT} />
|
|
24
|
+
{
|
|
25
|
+
tags.map((tag) =>
|
|
26
|
+
tag.content === null ? (
|
|
27
|
+
<script is:inline {...tag.attributes} />
|
|
28
|
+
) : (
|
|
29
|
+
<script is:inline set:html={tag.content} {...tag.attributes} />
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
<script>
|
|
34
|
+
import { startConsentClient } from "blume:consent-client";
|
|
35
|
+
import { startConsent } from "../../consent/client.ts";
|
|
36
|
+
|
|
37
|
+
startConsent(startConsentClient);
|
|
38
|
+
</script>
|