blume 2.0.3 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +142 -0
- package/README.md +1 -1
- package/dist/cli/{chunk-5f86nr5m.js → chunk-073rdvgb.js} +21 -48
- package/dist/cli/{chunk-5f86nr5m.js.map → chunk-073rdvgb.js.map} +4 -5
- package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
- package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
- package/dist/cli/chunk-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-9t7a85s3.js → chunk-313vnn40.js} +30 -19
- package/dist/cli/chunk-313vnn40.js.map +10 -0
- package/dist/cli/{chunk-tkacnehg.js → chunk-3v3ffbrj.js} +2 -2
- package/dist/cli/{chunk-q58y5e6a.js → chunk-3x6x3pph.js} +46 -24
- package/dist/cli/chunk-3x6x3pph.js.map +10 -0
- package/dist/cli/{chunk-1d7ve1dm.js → chunk-42t4jxdx.js} +37 -5
- package/dist/cli/chunk-42t4jxdx.js.map +12 -0
- package/dist/cli/{chunk-8ktnccpt.js → chunk-54ztw1nj.js} +21 -2
- package/dist/cli/chunk-54ztw1nj.js.map +10 -0
- package/dist/cli/{chunk-hm3vjy5s.js → chunk-5vjtgs6e.js} +28 -10
- package/dist/cli/{chunk-hm3vjy5s.js.map → chunk-5vjtgs6e.js.map} +4 -4
- package/dist/cli/chunk-66w9j7p6.js +322 -0
- package/dist/cli/chunk-66w9j7p6.js.map +14 -0
- package/dist/cli/{chunk-2hsdwb9n.js → chunk-7ppf1rva.js} +34 -18
- package/dist/cli/chunk-7ppf1rva.js.map +10 -0
- package/dist/cli/{chunk-r9rcc4w7.js → chunk-8y2kmjv8.js} +7 -7
- package/dist/cli/{chunk-r20tn01b.js → chunk-c1y538kd.js} +8 -2
- package/dist/cli/{chunk-r20tn01b.js.map → chunk-c1y538kd.js.map} +3 -3
- package/dist/cli/{chunk-2eytanqx.js → chunk-c887fj0s.js} +2 -2
- package/dist/cli/{chunk-p3v96n38.js → chunk-cfyg47zg.js} +16 -6
- package/dist/cli/chunk-cfyg47zg.js.map +10 -0
- package/dist/cli/chunk-cm89scdv.js +443 -0
- package/dist/cli/chunk-cm89scdv.js.map +11 -0
- package/dist/cli/{chunk-akbpwfxc.js → chunk-dzwehpy7.js} +2 -2
- package/dist/cli/{chunk-wrr3j9w9.js → chunk-e6atmn4h.js} +299 -72
- package/dist/cli/chunk-e6atmn4h.js.map +36 -0
- package/dist/cli/{chunk-tzmab476.js → chunk-e8xw9qkh.js} +8 -6
- package/dist/cli/{chunk-tzmab476.js.map → chunk-e8xw9qkh.js.map} +3 -3
- package/dist/cli/{chunk-6dsbexzp.js → chunk-e9q7a2ak.js} +17 -15
- package/dist/cli/{chunk-6dsbexzp.js.map → chunk-e9q7a2ak.js.map} +2 -2
- package/dist/cli/{chunk-pv29h0wf.js → chunk-ehweg4fr.js} +5155 -1249
- package/dist/cli/chunk-ehweg4fr.js.map +218 -0
- package/dist/cli/{chunk-c8chx29p.js → chunk-ep6nd0rn.js} +390 -318
- package/dist/cli/chunk-ep6nd0rn.js.map +36 -0
- package/dist/cli/{chunk-p73c0m7w.js → chunk-fdrkhpjy.js} +18 -15
- package/dist/cli/{chunk-p73c0m7w.js.map → chunk-fdrkhpjy.js.map} +3 -3
- package/dist/cli/{chunk-2z928egk.js → chunk-ft9vfys6.js} +5 -5
- package/dist/cli/{chunk-5m5nmvyq.js → chunk-gb420vvz.js} +316 -79
- package/dist/cli/chunk-gb420vvz.js.map +17 -0
- package/dist/cli/{chunk-vg9r4eb9.js → chunk-hebn0tpx.js} +86 -13
- package/dist/cli/chunk-hebn0tpx.js.map +14 -0
- package/dist/cli/{chunk-35d4wj9f.js → chunk-htrwgabr.js} +41 -23
- package/dist/cli/chunk-htrwgabr.js.map +15 -0
- package/dist/cli/{chunk-g4hq16wv.js → chunk-hx3xqnk9.js} +16 -14
- package/dist/cli/{chunk-g4hq16wv.js.map → chunk-hx3xqnk9.js.map} +2 -2
- package/dist/cli/chunk-j134vrp2.js +65 -0
- package/dist/cli/chunk-j134vrp2.js.map +10 -0
- package/dist/cli/{chunk-a58773jm.js → chunk-kma9f6sf.js} +2 -2
- package/dist/cli/chunk-kwrg63tm.js +31 -0
- package/dist/cli/chunk-kwrg63tm.js.map +10 -0
- package/dist/cli/{chunk-ey84smr6.js → chunk-mrcccgnh.js} +6 -6
- package/dist/cli/{chunk-ey84smr6.js.map → chunk-mrcccgnh.js.map} +1 -1
- package/dist/cli/{chunk-yfyb25rh.js → chunk-n0v7m8ry.js} +24 -8
- package/dist/cli/chunk-n0v7m8ry.js.map +14 -0
- package/dist/cli/{chunk-j85vccga.js → chunk-ncj7w596.js} +576 -367
- package/dist/cli/chunk-ncj7w596.js.map +35 -0
- package/dist/cli/{chunk-5xvm6tfj.js → chunk-nq1d20w2.js} +17 -15
- package/dist/cli/{chunk-5xvm6tfj.js.map → chunk-nq1d20w2.js.map} +2 -2
- package/dist/cli/{chunk-pehfxfta.js → chunk-nrns10mj.js} +6 -98
- package/dist/cli/chunk-nrns10mj.js.map +10 -0
- package/dist/cli/{chunk-crgn1q09.js → chunk-sds923fa.js} +4 -4
- package/dist/cli/chunk-t2m5j4pr.js +104 -0
- package/dist/cli/chunk-t2m5j4pr.js.map +11 -0
- package/dist/cli/{chunk-acanzt5p.js → chunk-tdqhyxfz.js} +6 -6
- package/dist/cli/chunk-tphac74f.js +126 -0
- package/dist/cli/chunk-tphac74f.js.map +11 -0
- package/dist/cli/chunk-w1r4vase.js +18 -0
- package/dist/cli/chunk-w1r4vase.js.map +10 -0
- package/dist/cli/{chunk-3em5wd2y.js → chunk-zccbjcj0.js} +4 -3
- package/dist/cli/{chunk-3em5wd2y.js.map → chunk-zccbjcj0.js.map} +2 -2
- package/dist/cli/index.js +31 -20
- package/dist/cli/index.js.map +5 -5
- package/dist/types/ai/agent-surface.d.ts +8 -7
- package/dist/types/ai/ai-catalog.d.ts +7 -0
- package/dist/types/ai/ask-context.d.ts +26 -0
- package/dist/types/ai/ask.d.ts +271 -58
- package/dist/types/ai/component-markdown.d.ts +2 -0
- package/dist/types/ai/index.d.ts +2 -2
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/ai/site-skill.d.ts +26 -0
- package/dist/types/ai/skills.d.ts +16 -0
- package/dist/types/astro/integration.d.ts +9 -0
- package/dist/types/astro/markdown-negotiation.d.ts +3 -0
- package/dist/types/astro/pages.d.ts +10 -0
- package/dist/types/captcha/hcaptcha.d.ts +27 -0
- package/dist/types/captcha/index.d.ts +28 -0
- package/dist/types/captcha/schema.d.ts +29 -0
- package/dist/types/captcha/turnstile.d.ts +28 -0
- package/dist/types/cli/init/scaffold.d.ts +1 -1
- package/dist/types/components/content/api-field.d.ts +62 -0
- package/dist/types/components/content/api-page.d.ts +100 -0
- package/dist/types/components/layout/nav-utils.d.ts +26 -0
- package/dist/types/components/openapi/code-samples.d.ts +29 -0
- package/dist/types/components/openapi/helpers.d.ts +158 -0
- package/dist/types/components/openapi/security.d.ts +95 -0
- package/dist/types/components/openapi/snippets.d.ts +60 -0
- package/dist/types/consent/ethyca.d.ts +48 -0
- package/dist/types/consent/index.d.ts +37 -0
- package/dist/types/consent/native.d.ts +28 -0
- package/dist/types/consent/osano.d.ts +42 -0
- package/dist/types/consent/schema.d.ts +110 -0
- package/dist/types/core/changelog-index.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +217 -23
- package/dist/types/core/data.d.ts +53 -4
- package/dist/types/core/footer.d.ts +8 -0
- package/dist/types/core/i18n-ui.d.ts +88 -0
- package/dist/types/core/includes.d.ts +8 -2
- package/dist/types/core/links.d.ts +8 -0
- package/dist/types/core/localizable.d.ts +7 -0
- package/dist/types/core/page-modes.d.ts +29 -0
- package/dist/types/core/probe.d.ts +2 -0
- package/dist/types/core/project-graph.d.ts +6 -0
- package/dist/types/core/redirect-patterns.d.ts +152 -0
- package/dist/types/core/related.d.ts +40 -0
- package/dist/types/core/schema.d.ts +670 -56
- package/dist/types/core/sources/cache.d.ts +3 -0
- package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
- package/dist/types/core/sources/contentful.d.ts +17 -1
- package/dist/types/core/sources/lexical.d.ts +5 -0
- package/dist/types/core/sources/normalize.d.ts +20 -1
- package/dist/types/core/sources/read.d.ts +5 -0
- package/dist/types/core/sources/remote.d.ts +37 -10
- package/dist/types/core/sources/sanity.d.ts +1 -1
- package/dist/types/core/types.d.ts +18 -1
- package/dist/types/core/variables.d.ts +42 -0
- package/dist/types/deploy/adapter-output.d.ts +2 -1
- package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
- package/dist/types/deploy/adapters/node.d.ts +58 -23
- package/dist/types/deploy/adapters/registry.d.ts +10 -0
- package/dist/types/deploy/adapters/types.d.ts +3 -2
- package/dist/types/deploy/node-headers.d.ts +13 -2
- package/dist/types/deploy/platforms/cloudflare.d.ts +15 -7
- package/dist/types/deploy/platforms/netlify.d.ts +7 -0
- package/dist/types/deploy/platforms/node.d.ts +3 -1
- package/dist/types/deploy/platforms/types.d.ts +18 -7
- package/dist/types/deploy/platforms/vercel.d.ts +26 -1
- package/dist/types/deploy/redirects.d.ts +55 -11
- package/dist/types/deploy/sitemap.d.ts +2 -1
- package/dist/types/deploy/vercel-negotiation.d.ts +51 -21
- package/dist/types/markdown/code-title.d.ts +17 -0
- package/dist/types/markdown/features.d.ts +3 -2
- package/dist/types/narration/build.d.ts +70 -0
- package/dist/types/narration/provider.d.ts +26 -0
- package/dist/types/narration/script.d.ts +154 -0
- package/dist/types/openapi/checks.d.ts +27 -0
- package/dist/types/openapi/graphql.d.ts +6 -0
- package/dist/types/openapi/model.d.ts +27 -10
- package/dist/types/openapi/overlay.d.ts +54 -0
- package/dist/types/openapi/parse.d.ts +16 -0
- package/dist/types/openapi/references.d.ts +30 -4
- package/dist/types/ratelimit/cloudflare.d.ts +39 -0
- package/dist/types/ratelimit/index.d.ts +27 -0
- package/dist/types/ratelimit/memory.d.ts +36 -0
- package/dist/types/ratelimit/schema.d.ts +119 -0
- package/dist/types/ratelimit/upstash.d.ts +46 -0
- package/dist/types/ratelimit/wrangler.d.ts +23 -0
- package/dist/types/reference/asyncapi.d.ts +10 -10
- package/dist/types/reference/graphql.d.ts +89 -11
- package/dist/types/reference/index.d.ts +1 -1
- package/dist/types/reference/openapi.d.ts +33 -12
- package/dist/types/reference/options.d.ts +82 -6
- package/dist/types/reference/scalar.d.ts +13 -0
- package/dist/types/reference/schema.d.ts +144 -36
- package/dist/types/search/adapters/registry.d.ts +3 -0
- package/dist/types/search/adapters/typesense.d.ts +14 -4
- package/dist/types/search/build.d.ts +3 -1
- package/dist/types/search/documents.d.ts +12 -1
- package/dist/types/search/orama-index.d.ts +13 -9
- package/dist/types/search/sync/algolia.d.ts +5 -2
- package/dist/types/search/sync/index.d.ts +3 -1
- package/dist/types/search/sync/typesense.d.ts +11 -7
- package/dist/types/seo/metatags.d.ts +20 -0
- package/dist/types/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +54 -0
- package/dist/types/sources/shared.d.ts +3 -0
- package/dist/types/sources/strapi.d.ts +2 -0
- package/docs/01-quickstart.mdx +4 -2
- package/docs/02-deployment.mdx +71 -17
- package/docs/03-upgrading.mdx +4 -4
- package/docs/04-migrating.mdx +1 -1
- package/docs/advanced/changelog.mdx +13 -2
- package/docs/advanced/skills.mdx +15 -1
- package/docs/cli/audit.mdx +51 -8
- package/docs/cli/doctor.mdx +5 -1
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/index.mdx +1 -0
- package/docs/cli/translate.mdx +1 -0
- package/docs/cli/validate.mdx +4 -3
- package/docs/configuration/analytics.mdx +3 -1
- package/docs/configuration/assistant.mdx +194 -38
- package/docs/configuration/consent.mdx +100 -0
- package/docs/configuration/customization.mdx +8 -6
- package/docs/configuration/export.mdx +5 -3
- package/docs/configuration/index.mdx +64 -5
- package/docs/configuration/meta.ts +3 -0
- package/docs/configuration/narration.mdx +119 -0
- package/docs/configuration/rate-limiting.mdx +90 -0
- package/docs/configuration/search.mdx +63 -12
- package/docs/configuration/theming.mdx +3 -1
- package/docs/content/components.mdx +108 -2
- package/docs/content/frontmatter.mdx +80 -2
- package/docs/content/i18n.mdx +39 -4
- package/docs/content/includes.mdx +16 -0
- package/docs/content/meta.mdx +1 -0
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +32 -10
- package/docs/content/sources/contentful.mdx +35 -0
- package/docs/content/sources/custom.mdx +43 -0
- package/docs/content/sources/github-releases.mdx +39 -0
- package/docs/content/sources/index.mdx +91 -0
- package/docs/content/sources/meta.ts +16 -0
- package/docs/content/sources/notion.mdx +32 -0
- package/docs/content/sources/obsidian.mdx +37 -0
- package/docs/content/sources/payload.mdx +31 -0
- package/docs/content/sources/remote-mdx.mdx +44 -0
- package/docs/content/sources/sanity.mdx +29 -0
- package/docs/content/sources/strapi.mdx +32 -0
- package/docs/content/syntax.mdx +69 -4
- package/docs/content/variables.mdx +41 -0
- package/docs/discoverability/agent-discovery.mdx +29 -2
- package/docs/discoverability/index.mdx +2 -2
- package/docs/discoverability/json-api.mdx +2 -0
- package/docs/discoverability/llms-txt.mdx +2 -2
- package/docs/discoverability/markdown.mdx +3 -1
- package/docs/discoverability/mcp.mdx +4 -0
- package/docs/discoverability/metadata.mdx +17 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/api-pages.mdx +74 -0
- package/docs/references/asyncapi.mdx +4 -2
- package/docs/references/graphql.mdx +14 -2
- package/docs/references/meta.ts +1 -1
- package/docs/references/openapi.mdx +148 -7
- package/docs/references/scalar.mdx +3 -1
- package/package.json +43 -8
- package/skills/blume/SKILL.md +3 -0
- package/skills/blume-migrate/SKILL.md +6 -6
- package/skills/blume-migrate/references/docusaurus.md +7 -7
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- package/skills/blume-migrate/references/mintlify.md +37 -19
- package/skills/blume-migrate/references/nextra.md +10 -7
- package/skills/blume-migrate/references/starlight.md +11 -8
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +60 -14
- package/skills/blume-write-skill/SKILL.md +82 -0
- package/src/ai/agent-readability.ts +2 -1
- package/src/ai/agent-surface.ts +9 -7
- package/src/ai/ai-catalog.ts +6 -3
- package/src/ai/ask-context.ts +103 -20
- package/src/ai/ask-data.ts +6 -0
- package/src/ai/ask-limits.ts +13 -0
- package/src/ai/ask-tools.ts +142 -0
- package/src/ai/ask.ts +354 -76
- package/src/ai/changelog-markdown.ts +11 -6
- package/src/ai/component-markdown.ts +76 -1
- package/src/ai/cors.ts +14 -3
- package/src/ai/index.ts +12 -0
- package/src/ai/llms.ts +6 -2
- package/src/ai/markdown.ts +2 -1
- package/src/ai/mcp/data.ts +6 -0
- package/src/ai/mcp/query.ts +14 -6
- package/src/ai/openapi-components.ts +36 -6
- package/src/ai/relative-links.ts +4 -38
- package/src/ai/site-skill.ts +274 -0
- package/src/ai/skills.ts +35 -2
- package/src/ai/write-skill.ts +97 -0
- package/src/astro/generate.ts +210 -66
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +42 -4
- package/src/astro/markdown-negotiation.ts +3 -0
- package/src/astro/module-types.ts +9 -1
- package/src/astro/pages.ts +23 -0
- package/src/astro/templates.ts +406 -108
- package/src/astro/variables.ts +31 -0
- package/src/audit/catalog.ts +38 -3
- package/src/audit/checks/contrast.ts +183 -0
- package/src/audit/checks/i18n.ts +2 -3
- package/src/audit/checks/indexability.ts +2 -3
- package/src/audit/checks/links.ts +19 -13
- package/src/audit/checks/network.ts +110 -6
- package/src/audit/checks/redirects.ts +6 -4
- package/src/audit/checks/robots.ts +58 -2
- package/src/audit/checks/sitemap.ts +2 -3
- package/src/audit/redirects.ts +46 -15
- package/src/audit/run.ts +5 -0
- package/src/audit/types.ts +4 -1
- package/src/audit/url.ts +14 -5
- package/src/blume-modules.d.ts +12 -1
- package/src/captcha/client.ts +152 -0
- package/src/captcha/hcaptcha.ts +36 -0
- package/src/captcha/index.ts +28 -0
- package/src/captcha/schema.ts +32 -0
- package/src/captcha/turnstile.ts +38 -0
- package/src/captcha/verify.ts +71 -0
- package/src/cli/args.ts +51 -0
- package/src/cli/build-failure.ts +23 -0
- package/src/cli/command-meta.ts +5 -0
- package/src/cli/commands/audit.ts +10 -1
- package/src/cli/commands/dev.ts +18 -2
- package/src/cli/commands/doctor.ts +67 -28
- package/src/cli/commands/eval.ts +36 -4
- package/src/cli/commands/init.ts +6 -4
- package/src/cli/commands/skill.ts +96 -0
- package/src/cli/commands/validate.ts +10 -1
- package/src/cli/index.ts +5 -0
- package/src/cli/init/scaffold.ts +3 -3
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +72 -30
- package/src/components/BlumePage.astro +2 -0
- package/src/components/content/ApiEndpoint.astro +18 -0
- package/src/components/content/ApiField.astro +79 -0
- package/src/components/content/ApiPlayground.astro +66 -0
- package/src/components/content/ApiRail.astro +19 -0
- package/src/components/content/Color.astro +1 -0
- package/src/components/content/FileTree.astro +1 -0
- package/src/components/content/GithubInfo.astro +1 -0
- package/src/components/content/Panel.astro +1 -0
- package/src/components/content/ParamField.astro +25 -0
- package/src/components/content/RequestExample.astro +17 -0
- package/src/components/content/ResponseExample.astro +17 -0
- package/src/components/content/ResponseField.astro +32 -0
- package/src/components/content/Step.astro +1 -0
- package/src/components/content/Tile.astro +1 -0
- package/src/components/content/Tooltip.astro +1 -0
- package/src/components/content/Tree.astro +1 -0
- package/src/components/content/TypeTable.astro +4 -1
- package/src/components/content/View.astro +21 -0
- package/src/components/content/ViewSwitcher.astro +168 -0
- package/src/components/content/api-field.ts +103 -0
- package/src/components/content/api-page.ts +248 -0
- package/src/components/content/mermaid-element.ts +15 -1
- package/src/components/content/view-switcher.ts +71 -0
- package/src/components/index.ts +2 -0
- package/src/components/islands/Assistant.astro +10 -1
- package/src/components/islands/assistant.tsx +104 -14
- package/src/components/islands/code-question.ts +62 -0
- package/src/components/islands/hooks.ts +136 -21
- package/src/components/islands/support-link.ts +68 -0
- package/src/components/layout/Analytics.astro +11 -2
- package/src/components/layout/Banner.astro +14 -10
- package/src/components/layout/Breadcrumbs.astro +6 -2
- package/src/components/layout/ConsentBanner.astro +62 -0
- package/src/components/layout/ConsentHead.astro +38 -0
- package/src/components/layout/Directory.astro +94 -0
- package/src/components/layout/DrawerSwitchers.astro +51 -0
- package/src/components/layout/Empty.astro +2 -2
- package/src/components/layout/Header.astro +122 -80
- package/src/components/layout/LanguageSwitcher.astro +48 -15
- package/src/components/layout/NarrationPlayer.astro +1085 -0
- package/src/components/layout/NavSelector.astro +33 -7
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/PageFeedback.astro +54 -49
- package/src/components/layout/PageLayout.astro +71 -3
- package/src/components/layout/ReferenceLayout.astro +17 -6
- package/src/components/layout/RelatedPages.astro +42 -0
- package/src/components/layout/RootLayout.astro +318 -47
- package/src/components/layout/Search.astro +47 -19
- package/src/components/layout/SiteFooter.astro +124 -0
- package/src/components/layout/SiteMetatags.astro +16 -0
- package/src/components/layout/TableOfContents.astro +24 -17
- package/src/components/layout/consent/native.ts +79 -0
- package/src/components/layout/consent/types.ts +37 -0
- package/src/components/layout/footer-links.ts +53 -0
- package/src/components/layout/footer-socials.ts +87 -0
- package/src/components/layout/head-scripts.ts +6 -1
- package/src/components/layout/locale-redirect.ts +107 -0
- package/src/components/layout/nav-utils.ts +83 -0
- package/src/components/layout/page-feedback.ts +96 -0
- package/src/components/layout/search/algolia.ts +10 -1
- package/src/components/layout/search/analytics.ts +126 -0
- package/src/components/layout/search/endpoint.ts +48 -5
- package/src/components/layout/search/flexsearch.ts +17 -5
- package/src/components/layout/search/orama-cloud.ts +26 -14
- package/src/components/layout/search/pagefind.ts +114 -5
- package/src/components/layout/search/types.ts +32 -0
- package/src/components/layout/search/typesense.ts +19 -3
- package/src/components/openapi/AsyncApiOperation.astro +14 -7
- package/src/components/openapi/Authorization.astro +10 -0
- package/src/components/openapi/GraphqlOperation.astro +5 -0
- package/src/components/openapi/Operation.astro +184 -13
- package/src/components/openapi/ParametersTable.astro +8 -4
- package/src/components/openapi/Playground.astro +7 -1
- package/src/components/openapi/RequestBody.astro +24 -5
- package/src/components/openapi/RequestPanel.astro +59 -7
- package/src/components/openapi/Responses.astro +7 -4
- package/src/components/openapi/SchemaTable.astro +15 -3
- package/src/components/openapi/async-snippets.ts +65 -9
- package/src/components/openapi/async.ts +123 -29
- package/src/components/openapi/code-samples.ts +97 -0
- package/src/components/openapi/graphql-helpers.ts +35 -12
- package/src/components/openapi/helpers.ts +36 -2
- package/src/components/openapi/message-model.ts +54 -11
- package/src/components/openapi/message.ts +14 -4
- package/src/components/openapi/operation-model.ts +16 -7
- package/src/components/openapi/playground-client.ts +24 -5
- package/src/components/openapi/playground-schema.ts +1 -1
- package/src/components/openapi/schema-tree.ts +44 -14
- package/src/components/openapi/security.ts +16 -3
- package/src/components/openapi/snippets.ts +873 -10
- package/src/components/openapi/validate-json.ts +4 -3
- package/src/components/props.ts +6 -0
- package/src/consent/client.ts +95 -0
- package/src/consent/clients.ts +19 -0
- package/src/consent/ethyca.ts +71 -0
- package/src/consent/head.ts +24 -0
- package/src/consent/index.ts +37 -0
- package/src/consent/init.ts +29 -0
- package/src/consent/native.ts +36 -0
- package/src/consent/osano.ts +59 -0
- package/src/consent/schema.ts +30 -0
- package/src/core/builtin-tags.ts +5 -0
- package/src/core/changelog-index.ts +31 -1
- package/src/core/client-address.ts +19 -0
- package/src/core/component-diagnostics.ts +53 -0
- package/src/core/component-overrides.ts +32 -0
- package/src/core/config-input.ts +225 -23
- package/src/core/data.ts +46 -4
- package/src/core/diagnostics.ts +8 -2
- package/src/core/footer.ts +26 -0
- package/src/core/graph.ts +39 -36
- package/src/core/i18n-ui.ts +84 -2
- package/src/core/includes.ts +21 -4
- package/src/core/links.ts +63 -19
- package/src/core/localizable.ts +26 -0
- package/src/core/navigation.ts +43 -12
- package/src/core/page-modes.ts +56 -0
- package/src/core/probe.ts +26 -6
- package/src/core/project-graph.ts +163 -16
- package/src/core/redirect-patterns.ts +520 -0
- package/src/core/related.ts +131 -0
- package/src/core/schema.ts +339 -66
- package/src/core/sources/cache.ts +6 -0
- package/src/core/sources/contentful-rich-text.ts +29 -6
- package/src/core/sources/contentful.ts +135 -25
- package/src/core/sources/filesystem.ts +26 -4
- package/src/core/sources/github-releases.ts +12 -1
- package/src/core/sources/lexical.ts +15 -10
- package/src/core/sources/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +228 -41
- package/src/core/sources/notion.ts +7 -1
- package/src/core/sources/obsidian.ts +16 -3
- package/src/core/sources/payload.ts +38 -15
- package/src/core/sources/read.ts +8 -3
- package/src/core/sources/remote.ts +72 -15
- package/src/core/sources/resolve.ts +11 -1
- package/src/core/sources/sanity.ts +43 -25
- package/src/core/sources/strapi.ts +2 -2
- package/src/core/types.ts +19 -1
- package/src/core/ui-packs/ar.ts +44 -0
- package/src/core/ui-packs/bg.ts +46 -0
- package/src/core/ui-packs/bn.ts +44 -0
- package/src/core/ui-packs/ca.ts +47 -0
- package/src/core/ui-packs/cs.ts +45 -0
- package/src/core/ui-packs/da.ts +46 -0
- package/src/core/ui-packs/de.ts +47 -0
- package/src/core/ui-packs/el.ts +46 -0
- package/src/core/ui-packs/es.ts +47 -0
- package/src/core/ui-packs/fa.ts +46 -0
- package/src/core/ui-packs/fi.ts +46 -0
- package/src/core/ui-packs/fr.ts +47 -0
- package/src/core/ui-packs/he.ts +44 -0
- package/src/core/ui-packs/hi.ts +45 -0
- package/src/core/ui-packs/hr.ts +46 -0
- package/src/core/ui-packs/hu.ts +45 -0
- package/src/core/ui-packs/id.ts +46 -0
- package/src/core/ui-packs/it.ts +46 -0
- package/src/core/ui-packs/ja.ts +46 -0
- package/src/core/ui-packs/ko.ts +45 -0
- package/src/core/ui-packs/nl.ts +47 -0
- package/src/core/ui-packs/no.ts +45 -0
- package/src/core/ui-packs/pl.ts +46 -0
- package/src/core/ui-packs/pt-br.ts +47 -0
- package/src/core/ui-packs/pt.ts +46 -0
- package/src/core/ui-packs/ro.ts +46 -0
- package/src/core/ui-packs/ru.ts +46 -0
- package/src/core/ui-packs/sk.ts +45 -0
- package/src/core/ui-packs/sr.ts +46 -0
- package/src/core/ui-packs/sv.ts +45 -0
- package/src/core/ui-packs/th.ts +44 -0
- package/src/core/ui-packs/tr.ts +46 -0
- package/src/core/ui-packs/uk.ts +46 -0
- package/src/core/ui-packs/vi.ts +46 -0
- package/src/core/ui-packs/zh-tw.ts +44 -0
- package/src/core/ui-packs/zh.ts +44 -0
- package/src/core/variables.ts +103 -0
- package/src/deploy/adapter-output.ts +7 -2
- package/src/deploy/adapters/cloudflare.ts +7 -5
- package/src/deploy/adapters/node.ts +47 -9
- package/src/deploy/adapters/types.ts +7 -3
- package/src/deploy/artifacts.ts +79 -9
- package/src/deploy/cloudflare-negotiation.ts +35 -5
- package/src/deploy/headers.ts +2 -1
- package/src/deploy/node-headers.ts +63 -9
- package/src/deploy/platforms/cloudflare.ts +45 -9
- package/src/deploy/platforms/netlify.ts +35 -2
- package/src/deploy/platforms/node.ts +12 -2
- package/src/deploy/platforms/types.ts +21 -7
- package/src/deploy/platforms/vercel.ts +149 -17
- package/src/deploy/redirects.ts +151 -24
- package/src/deploy/rss.ts +3 -1
- package/src/deploy/sitemap.ts +39 -5
- package/src/deploy/vercel-negotiation.ts +326 -102
- package/src/eval/agents.ts +18 -1
- package/src/eval/prompts.ts +6 -3
- package/src/eval/report.ts +34 -13
- package/src/eval/run.ts +5 -0
- package/src/markdown/api-rail.ts +259 -0
- package/src/markdown/code-title.ts +35 -9
- package/src/markdown/features.ts +3 -2
- package/src/markdown/fence-meta.ts +2 -0
- package/src/markdown/include.ts +11 -1
- package/src/markdown/index.ts +28 -5
- package/src/markdown/math.ts +4 -4
- package/src/markdown/relative-links.ts +3 -2
- package/src/markdown/variables.ts +56 -0
- package/src/markdown/views.ts +82 -0
- package/src/narration/build.ts +412 -0
- package/src/narration/player.ts +249 -0
- package/src/narration/provider.ts +29 -0
- package/src/narration/script.ts +592 -0
- package/src/og/card.ts +7 -6
- package/src/openapi/checks.ts +273 -0
- package/src/openapi/graphql-build.ts +21 -0
- package/src/openapi/graphql.ts +11 -0
- package/src/openapi/model.ts +72 -27
- package/src/openapi/overlay.ts +256 -0
- package/src/openapi/parse.ts +182 -4
- package/src/openapi/references.ts +64 -8
- package/src/openapi/render-mdx.ts +5 -1
- package/src/openapi/scalar.ts +40 -6
- package/src/openapi/source.ts +40 -15
- package/src/ratelimit/cloudflare.ts +54 -0
- package/src/ratelimit/index.ts +30 -0
- package/src/ratelimit/memory.ts +45 -0
- package/src/ratelimit/runtime.ts +247 -0
- package/src/ratelimit/schema.ts +28 -0
- package/src/ratelimit/upstash.ts +60 -0
- package/src/ratelimit/wrangler.ts +70 -0
- package/src/reference/asyncapi.ts +2 -2
- package/src/reference/graphql.ts +20 -3
- package/src/reference/index.ts +1 -0
- package/src/reference/openapi.ts +21 -7
- package/src/reference/options.ts +80 -8
- package/src/reference/scalar.ts +33 -8
- package/src/registry/eject.ts +23 -5
- package/src/registry/registry.ts +7 -0
- package/src/search/adapters/locale-scope.ts +30 -0
- package/src/search/adapters/typesense.ts +14 -4
- package/src/search/build.ts +21 -3
- package/src/search/documents.ts +26 -2
- package/src/search/orama-index.ts +191 -34
- package/src/search/pagefind-ranking.ts +39 -0
- package/src/search/source-pages.ts +29 -0
- package/src/search/sync/algolia.ts +111 -10
- package/src/search/sync/index.ts +52 -13
- package/src/search/sync/orama-cloud.ts +3 -0
- package/src/search/sync/typesense.ts +76 -26
- package/src/seo/metatags.ts +67 -0
- package/src/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/shared.ts +3 -0
- package/src/theme/chrome-icons.ts +1 -0
- package/src/theme/color.ts +500 -0
- package/src/theme/entry.ts +85 -6
- package/src/theme/palette.ts +121 -45
- package/src/translate/prompts.ts +1 -0
- package/src/translate/validate.ts +31 -3
- package/src/upgrade/upgrade.ts +4 -171
- package/dist/cli/chunk-1d7ve1dm.js.map +0 -12
- package/dist/cli/chunk-2hsdwb9n.js.map +0 -10
- package/dist/cli/chunk-35d4wj9f.js.map +0 -15
- package/dist/cli/chunk-364znk6q.js +0 -75
- package/dist/cli/chunk-364znk6q.js.map +0 -11
- package/dist/cli/chunk-5m5nmvyq.js.map +0 -13
- package/dist/cli/chunk-8ktnccpt.js.map +0 -10
- package/dist/cli/chunk-9t7a85s3.js.map +0 -10
- package/dist/cli/chunk-b07cmahc.js +0 -145
- package/dist/cli/chunk-b07cmahc.js.map +0 -11
- package/dist/cli/chunk-c8chx29p.js.map +0 -36
- package/dist/cli/chunk-e04dxsz1.js +0 -39
- package/dist/cli/chunk-e04dxsz1.js.map +0 -10
- package/dist/cli/chunk-j85vccga.js.map +0 -36
- package/dist/cli/chunk-p3v96n38.js.map +0 -10
- package/dist/cli/chunk-pehfxfta.js.map +0 -10
- package/dist/cli/chunk-pv29h0wf.js.map +0 -190
- package/dist/cli/chunk-q58y5e6a.js.map +0 -10
- package/dist/cli/chunk-vg9r4eb9.js.map +0 -14
- package/dist/cli/chunk-wrr3j9w9.js.map +0 -35
- package/dist/cli/chunk-yfyb25rh.js.map +0 -14
- package/docs/content/sources.mdx +0 -334
- /package/dist/cli/{chunk-tkacnehg.js.map → chunk-3v3ffbrj.js.map} +0 -0
- /package/dist/cli/{chunk-r9rcc4w7.js.map → chunk-8y2kmjv8.js.map} +0 -0
- /package/dist/cli/{chunk-2eytanqx.js.map → chunk-c887fj0s.js.map} +0 -0
- /package/dist/cli/{chunk-akbpwfxc.js.map → chunk-dzwehpy7.js.map} +0 -0
- /package/dist/cli/{chunk-2z928egk.js.map → chunk-ft9vfys6.js.map} +0 -0
- /package/dist/cli/{chunk-a58773jm.js.map → chunk-kma9f6sf.js.map} +0 -0
- /package/dist/cli/{chunk-crgn1q09.js.map → chunk-sds923fa.js.map} +0 -0
- /package/dist/cli/{chunk-acanzt5p.js.map → chunk-tdqhyxfz.js.map} +0 -0
|
@@ -4,10 +4,14 @@ import { useEffect, useRef, useState } from "react";
|
|
|
4
4
|
import type { FormEvent, KeyboardEvent as ReactKeyboardEvent } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
6
|
|
|
7
|
+
import type { CaptchaSettings } from "../../captcha/schema.ts";
|
|
7
8
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
8
9
|
import { copyText } from "../copy-feedback.ts";
|
|
9
10
|
import { joinBase, mountBase } from "./base-path.ts";
|
|
11
|
+
import { splitCode, withCode } from "./code-question.ts";
|
|
12
|
+
import type { CodeAttachment } from "./code-question.ts";
|
|
10
13
|
import { useAssistant } from "./hooks.ts";
|
|
14
|
+
import { supportHref } from "./support-link.ts";
|
|
11
15
|
|
|
12
16
|
/** A resolved empty-state prompt; `icon` is ready-to-inline SVG (or null). */
|
|
13
17
|
interface Suggestion {
|
|
@@ -25,6 +29,8 @@ interface AssistantIcons {
|
|
|
25
29
|
clear: string;
|
|
26
30
|
close: string;
|
|
27
31
|
copy: string;
|
|
32
|
+
/** The attached code chip's dismiss glyph. */
|
|
33
|
+
remove?: string;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
// Empty bodies so the island still renders (iconless) if instantiated without
|
|
@@ -45,11 +51,18 @@ const DEFAULT_ASK: UIStrings["assistant"] = {
|
|
|
45
51
|
copy: "Copy conversation",
|
|
46
52
|
empty: "Ask a question about the docs.",
|
|
47
53
|
error: "Sorry, something went wrong.",
|
|
54
|
+
explainCode: "Explain this code.",
|
|
48
55
|
label: "Ask a question",
|
|
56
|
+
open: "Open assistant",
|
|
49
57
|
placeholder: "Ask a question…",
|
|
58
|
+
rateLimited: "You've asked a lot of questions. Try again in a few minutes.",
|
|
59
|
+
removeCode: "Remove code",
|
|
50
60
|
send: "Send",
|
|
61
|
+
support: "Contact support",
|
|
62
|
+
supportSubject: "Question from the docs",
|
|
51
63
|
tip: "Tip: You can open and close chat with",
|
|
52
64
|
title: "Assistant",
|
|
65
|
+
verifyFailed: "We couldn't check that you're human. Try again.",
|
|
53
66
|
you: "You",
|
|
54
67
|
};
|
|
55
68
|
|
|
@@ -140,22 +153,54 @@ const ICON_BUTTON_CLASS =
|
|
|
140
153
|
const ANSWER_CLASS =
|
|
141
154
|
"prose prose-sm max-w-none text-foreground [&_a]:inline-flex [&_a]:items-center [&_a]:gap-1 [&_a]:rounded-full [&_a]:bg-muted [&_a]:px-2 [&_a]:py-1 [&_a]:align-middle [&_a]:font-medium [&_a]:text-[0.7rem] [&_a]:leading-none [&_a]:text-muted-foreground! [&_a]:no-underline! [&_a:hover]:text-foreground!";
|
|
142
155
|
|
|
156
|
+
/** A reader's message bubble. */
|
|
157
|
+
const USER_MESSAGE_CLASS =
|
|
158
|
+
"rounded-blume bg-muted text-foreground max-w-[85%] self-end px-3 py-2 text-sm whitespace-pre-wrap";
|
|
159
|
+
|
|
160
|
+
/** What `blume:open-assistant` may carry. */
|
|
161
|
+
interface OpenAssistantDetail {
|
|
162
|
+
code?: CodeAttachment;
|
|
163
|
+
query?: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** A reader's message, with any code they asked about shown as a block. */
|
|
167
|
+
const UserMessage = ({ content }: { content: string }) => {
|
|
168
|
+
const { code, text } = splitCode(content);
|
|
169
|
+
if (!code) {
|
|
170
|
+
return <div className={USER_MESSAGE_CLASS}>{text}</div>;
|
|
171
|
+
}
|
|
172
|
+
return (
|
|
173
|
+
<div className={USER_MESSAGE_CLASS}>
|
|
174
|
+
{text}
|
|
175
|
+
<pre className="bg-background mt-2 max-h-40 overflow-auto rounded-md p-2 font-mono text-xs whitespace-pre">
|
|
176
|
+
{code.source}
|
|
177
|
+
</pre>
|
|
178
|
+
</div>
|
|
179
|
+
);
|
|
180
|
+
};
|
|
181
|
+
|
|
143
182
|
const Assistant = ({
|
|
183
|
+
captcha,
|
|
144
184
|
endpoint = DEFAULT_ASK_ENDPOINT,
|
|
145
185
|
icons = EMPTY_ICONS,
|
|
146
186
|
strings,
|
|
147
187
|
suggestions = EMPTY_SUGGESTIONS,
|
|
188
|
+
support,
|
|
148
189
|
}: {
|
|
190
|
+
captcha?: CaptchaSettings;
|
|
149
191
|
endpoint?: string;
|
|
150
192
|
icons?: AssistantIcons;
|
|
151
193
|
strings?: UIStrings["assistant"];
|
|
152
194
|
suggestions?: Suggestion[];
|
|
195
|
+
support?: string;
|
|
153
196
|
}) => {
|
|
154
197
|
// Merge per key (not `strings ?? …`) so a dictionary from a stale snapshot
|
|
155
198
|
// that predates newer keys still resolves every label to its English default.
|
|
156
199
|
const t = { ...DEFAULT_ASK, ...strings };
|
|
157
200
|
const [open, setOpen] = useState(false);
|
|
158
201
|
const [input, setInput] = useState("");
|
|
202
|
+
// A code block the reader is asking about, sent with the next question.
|
|
203
|
+
const [code, setCode] = useState<CodeAttachment | null>(null);
|
|
159
204
|
// The streaming client — request shaping, optimistic assistant bubble,
|
|
160
205
|
// stale-stream/abort guards, error-body handling — is the public useAssistant
|
|
161
206
|
// hook, so the built-in panel and custom UIs share one implementation.
|
|
@@ -164,7 +209,14 @@ const Assistant = ({
|
|
|
164
209
|
loading: busy,
|
|
165
210
|
messages,
|
|
166
211
|
reset,
|
|
167
|
-
|
|
212
|
+
thread,
|
|
213
|
+
} = useAssistant({
|
|
214
|
+
captcha,
|
|
215
|
+
endpoint,
|
|
216
|
+
errorMessage: t.error,
|
|
217
|
+
rateLimitMessage: t.rateLimited,
|
|
218
|
+
verifyMessage: t.verifyFailed,
|
|
219
|
+
});
|
|
168
220
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
169
221
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
170
222
|
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
@@ -193,14 +245,18 @@ const Assistant = ({
|
|
|
193
245
|
return () => document.removeEventListener("astro:after-swap", onSwap);
|
|
194
246
|
}, []);
|
|
195
247
|
|
|
196
|
-
// The search modal forwards its query so "Assistant: <query>" carries
|
|
248
|
+
// The search modal forwards its query so "Assistant: <query>" carries
|
|
249
|
+
// straight in, and a code block's Ask button attaches its code.
|
|
197
250
|
useEffect(() => {
|
|
198
251
|
const handler = (event: Event) => {
|
|
199
252
|
// SAFETY: `blume:open-assistant` is only ever dispatched as a CustomEvent
|
|
200
|
-
// whose optional detail carries the search query.
|
|
201
|
-
const
|
|
202
|
-
if (query) {
|
|
203
|
-
setInput(query);
|
|
253
|
+
// whose optional detail carries the search query or a code block.
|
|
254
|
+
const { detail } = event as CustomEvent<OpenAssistantDetail | null>;
|
|
255
|
+
if (detail?.query) {
|
|
256
|
+
setInput(detail.query);
|
|
257
|
+
}
|
|
258
|
+
if (detail?.code) {
|
|
259
|
+
setCode(detail.code);
|
|
204
260
|
}
|
|
205
261
|
setOpen(true);
|
|
206
262
|
};
|
|
@@ -366,12 +422,14 @@ const Assistant = ({
|
|
|
366
422
|
}, [messages, portalTarget]);
|
|
367
423
|
|
|
368
424
|
const runQuestion = (raw: string) => {
|
|
369
|
-
|
|
425
|
+
// A question about attached code may leave the words to us.
|
|
426
|
+
const question = raw.trim() || (code ? t.explainCode : "");
|
|
370
427
|
if (!question || busy) {
|
|
371
428
|
return;
|
|
372
429
|
}
|
|
373
|
-
void ask(question);
|
|
430
|
+
void ask(code ? withCode(question, code) : question);
|
|
374
431
|
setInput("");
|
|
432
|
+
setCode(null);
|
|
375
433
|
};
|
|
376
434
|
|
|
377
435
|
const clearConversation = () => {
|
|
@@ -464,13 +522,11 @@ const Assistant = ({
|
|
|
464
522
|
last entry while streaming, or clears wholesale on reset. */}
|
|
465
523
|
{messages.map((message, index) =>
|
|
466
524
|
message.role === "user" ? (
|
|
467
|
-
<
|
|
468
|
-
|
|
525
|
+
<UserMessage
|
|
526
|
+
content={message.content}
|
|
469
527
|
// oxlint-disable-next-line react/no-array-index-key -- append-only list, see above
|
|
470
528
|
key={index}
|
|
471
|
-
|
|
472
|
-
{message.content}
|
|
473
|
-
</div>
|
|
529
|
+
/>
|
|
474
530
|
) : (
|
|
475
531
|
// oxlint-disable-next-line react/no-array-index-key -- append-only list, see above
|
|
476
532
|
<div className={ANSWER_CLASS} key={index}>
|
|
@@ -491,6 +547,23 @@ const Assistant = ({
|
|
|
491
547
|
</div>
|
|
492
548
|
)
|
|
493
549
|
)}
|
|
550
|
+
{support && !busy && (
|
|
551
|
+
<a
|
|
552
|
+
className="text-muted-foreground hover:text-foreground self-start text-xs underline underline-offset-2 transition-colors"
|
|
553
|
+
href={supportHref(support, {
|
|
554
|
+
ai: t.ai,
|
|
555
|
+
messages,
|
|
556
|
+
subject: t.supportSubject,
|
|
557
|
+
thread,
|
|
558
|
+
you: t.you,
|
|
559
|
+
})}
|
|
560
|
+
{...(support.startsWith("mailto:") || support.startsWith("/")
|
|
561
|
+
? {}
|
|
562
|
+
: { rel: "noopener noreferrer", target: "_blank" })}
|
|
563
|
+
>
|
|
564
|
+
{t.support}
|
|
565
|
+
</a>
|
|
566
|
+
)}
|
|
494
567
|
</div>
|
|
495
568
|
) : (
|
|
496
569
|
<div className="mt-auto flex flex-col gap-0.5 p-4">
|
|
@@ -531,6 +604,21 @@ const Assistant = ({
|
|
|
531
604
|
className="border-border relative shrink-0 border-t"
|
|
532
605
|
onSubmit={onSubmit}
|
|
533
606
|
>
|
|
607
|
+
{code && (
|
|
608
|
+
<div className="flex items-center gap-1.5 px-4 pt-3">
|
|
609
|
+
<span className="rounded-blume bg-muted text-muted-foreground max-w-full truncate px-2 py-1 font-mono text-xs">
|
|
610
|
+
{code.title || code.language || t.explainCode}
|
|
611
|
+
</span>
|
|
612
|
+
<button
|
|
613
|
+
aria-label={t.removeCode}
|
|
614
|
+
className={ICON_BUTTON_CLASS}
|
|
615
|
+
onClick={() => setCode(null)}
|
|
616
|
+
type="button"
|
|
617
|
+
>
|
|
618
|
+
<Glyph path={icons.remove ?? ""} size={14} />
|
|
619
|
+
</button>
|
|
620
|
+
</div>
|
|
621
|
+
)}
|
|
534
622
|
<textarea
|
|
535
623
|
aria-label={t.label}
|
|
536
624
|
className="text-foreground placeholder:text-muted-foreground max-h-48 min-h-[5rem] w-full resize-none bg-transparent px-4 py-3.5 pe-14 text-sm outline-none pointer-coarse:text-base"
|
|
@@ -544,7 +632,7 @@ const Assistant = ({
|
|
|
544
632
|
<button
|
|
545
633
|
aria-label={t.send}
|
|
546
634
|
className="rounded-blume bg-foreground text-background absolute end-3 bottom-3 inline-flex h-8 w-8 cursor-pointer items-center justify-center transition-opacity disabled:cursor-not-allowed disabled:opacity-40"
|
|
547
|
-
disabled={busy || input.trim().length === 0}
|
|
635
|
+
disabled={busy || (input.trim().length === 0 && !code)}
|
|
548
636
|
type="submit"
|
|
549
637
|
>
|
|
550
638
|
<Glyph path={icons.arrowUp} />
|
|
@@ -559,6 +647,8 @@ const Assistant = ({
|
|
|
559
647
|
aria-expanded={open}
|
|
560
648
|
aria-label={t.title}
|
|
561
649
|
className={TRIGGER_CLASS}
|
|
650
|
+
// Named by the header's tooltip, like the buttons beside it.
|
|
651
|
+
data-blume-tooltip={t.open}
|
|
562
652
|
onClick={() => setOpen((value) => !value)}
|
|
563
653
|
ref={triggerRef}
|
|
564
654
|
type="button"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Questions about a code block. A code block's Ask button opens the assistant
|
|
3
|
+
* with the block attached (`blume:open-assistant` with `detail.code`); the
|
|
4
|
+
* question goes out with the code folded in as a fenced block after the
|
|
5
|
+
* reader's words, which is how the model sees it, and the conversation shows
|
|
6
|
+
* the two apart again.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** A code block the reader asked about. */
|
|
10
|
+
export interface CodeAttachment {
|
|
11
|
+
/** The fence language (`ts`), or empty when the block has none. */
|
|
12
|
+
language: string;
|
|
13
|
+
source: string;
|
|
14
|
+
/** The block's title (`server.ts`), when it has one. */
|
|
15
|
+
title?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The most code one question carries, well inside the route's budget. */
|
|
19
|
+
export const MAX_ATTACHED_CODE = 6000;
|
|
20
|
+
|
|
21
|
+
/** A user message, split into the reader's words and any attached code. */
|
|
22
|
+
export interface SplitQuestion {
|
|
23
|
+
code?: { language: string; source: string };
|
|
24
|
+
text: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** A fence longer than any backtick run in the code, so the code can't close it. */
|
|
28
|
+
const fenceFor = (source: string): string => {
|
|
29
|
+
const longest = Math.max(
|
|
30
|
+
2,
|
|
31
|
+
...[...source.matchAll(/`+/gu)].map(([run]) => run.length)
|
|
32
|
+
);
|
|
33
|
+
return "`".repeat(longest + 1);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** The question as sent: the reader's words, then the code as a fenced block. */
|
|
37
|
+
export const withCode = (question: string, code: CodeAttachment): string => {
|
|
38
|
+
const source =
|
|
39
|
+
code.source.length > MAX_ATTACHED_CODE
|
|
40
|
+
? `${code.source.slice(0, MAX_ATTACHED_CODE)}\n…`
|
|
41
|
+
: code.source.replace(/\n+$/u, "");
|
|
42
|
+
const fence = fenceFor(source);
|
|
43
|
+
return `${question}\n\n${fence}${code.language}\n${source}\n${fence}`;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const TRAILING_FENCE =
|
|
47
|
+
/\n\n(?<fence>`{3,})(?<language>[^\n`]*)\n(?<source>[\s\S]*)\n\k<fence>$/u;
|
|
48
|
+
|
|
49
|
+
/** A user message split back into the words and the attached code. */
|
|
50
|
+
export const splitCode = (content: string): SplitQuestion => {
|
|
51
|
+
const match = TRAILING_FENCE.exec(content);
|
|
52
|
+
if (!match?.groups) {
|
|
53
|
+
return { text: content };
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
code: {
|
|
57
|
+
language: match.groups.language ?? "",
|
|
58
|
+
source: match.groups.source ?? "",
|
|
59
|
+
},
|
|
60
|
+
text: content.slice(0, match.index),
|
|
61
|
+
};
|
|
62
|
+
};
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
ASK_MAX_MESSAGES,
|
|
5
|
+
ASK_MAX_MESSAGES_CHARS,
|
|
6
|
+
} from "../../ai/ask-limits.ts";
|
|
7
|
+
import { captchaToken } from "../../captcha/client.ts";
|
|
8
|
+
import type { CaptchaSettings } from "../../captcha/schema.ts";
|
|
3
9
|
import type { BlumeClientData } from "../../core/data.ts";
|
|
4
10
|
import { track } from "../layout/analytics-client.ts";
|
|
5
11
|
import type { SearchFn, SearchResult } from "../layout/search/types.ts";
|
|
6
12
|
import { joinBase, stripBase } from "./base-path.ts";
|
|
13
|
+
import { newThreadId } from "./support-link.ts";
|
|
7
14
|
|
|
8
15
|
/**
|
|
9
16
|
* React hooks for Blume islands.
|
|
@@ -159,11 +166,22 @@ export interface UseAssistant {
|
|
|
159
166
|
loading: boolean;
|
|
160
167
|
messages: AskMessage[];
|
|
161
168
|
reset: () => void;
|
|
169
|
+
/**
|
|
170
|
+
* The conversation's id, new after every `reset()`. The `ask` analytics
|
|
171
|
+
* events carry it as `thread`, and the support link passes it along.
|
|
172
|
+
*/
|
|
173
|
+
thread: string;
|
|
162
174
|
}
|
|
163
175
|
|
|
164
176
|
const DEFAULT_ASK_ENDPOINT = joinBase(import.meta.env.BASE_URL, "api/ask");
|
|
165
177
|
|
|
166
178
|
export interface UseAssistantOptions {
|
|
179
|
+
/**
|
|
180
|
+
* The site's bot check (`ai.assistant.captcha`), as `useBlume().config
|
|
181
|
+
* .assistant.captcha` holds it: each question gets a fresh token, sent as
|
|
182
|
+
* `captcha` in the request body.
|
|
183
|
+
*/
|
|
184
|
+
captcha?: CaptchaSettings | null;
|
|
167
185
|
/** Existing assistant endpoint; defaults to Blume's generated `/api/ask`. */
|
|
168
186
|
endpoint?: string;
|
|
169
187
|
/**
|
|
@@ -172,11 +190,38 @@ export interface UseAssistantOptions {
|
|
|
172
190
|
* dictionary string.
|
|
173
191
|
*/
|
|
174
192
|
errorMessage?: string;
|
|
193
|
+
/**
|
|
194
|
+
* Shown as the assistant's answer when the route's rate limit turns the
|
|
195
|
+
* question away (`429`). Defaults to an English notice; the built-in
|
|
196
|
+
* island passes its localized dictionary string.
|
|
197
|
+
*/
|
|
198
|
+
rateLimitMessage?: string;
|
|
199
|
+
/**
|
|
200
|
+
* Shown as the assistant's answer when the bot check fails, in the browser
|
|
201
|
+
* or at the route (`403`). Defaults to an English notice; the built-in
|
|
202
|
+
* island passes its localized dictionary string.
|
|
203
|
+
*/
|
|
204
|
+
verifyMessage?: string;
|
|
175
205
|
}
|
|
176
206
|
|
|
177
207
|
/** Shown as the assistant's answer when the request fails or throws. */
|
|
178
208
|
const ASK_ERROR = "Something went wrong answering that. Please try again.";
|
|
179
209
|
|
|
210
|
+
/** Shown as the assistant's answer when the rate limit turns a question away. */
|
|
211
|
+
const ASK_RATE_LIMITED =
|
|
212
|
+
"You've asked a lot of questions. Try again in a few minutes.";
|
|
213
|
+
|
|
214
|
+
/** Shown as the assistant's answer when the bot check fails. */
|
|
215
|
+
const ASK_VERIFY_FAILED = "We couldn't check that you're human. Try again.";
|
|
216
|
+
|
|
217
|
+
/** What the assistant sends the route. */
|
|
218
|
+
interface AskPayload {
|
|
219
|
+
/** The bot check's token, when the site has one. */
|
|
220
|
+
captcha?: string;
|
|
221
|
+
messages: AskMessage[];
|
|
222
|
+
page: { path: string };
|
|
223
|
+
}
|
|
224
|
+
|
|
180
225
|
/** How the assistant's generated route opens its missing-credential notice. */
|
|
181
226
|
const NOT_CONFIGURED = /^The assistant is not configured/u;
|
|
182
227
|
|
|
@@ -199,10 +244,52 @@ const unavailableNotice = async (
|
|
|
199
244
|
return NOT_CONFIGURED.test(text) && text.length <= 200 ? text : null;
|
|
200
245
|
};
|
|
201
246
|
|
|
247
|
+
/** The notices the route's refusals map to. */
|
|
248
|
+
interface RefusalNotices {
|
|
249
|
+
rateLimitMessage: string;
|
|
250
|
+
verifyMessage: string;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* The reader-facing notice for the route turning a question away: the rate
|
|
255
|
+
* limit (`429`) or the bot check (`403`), or `null` for any other status.
|
|
256
|
+
*/
|
|
257
|
+
const refusalNotice = (
|
|
258
|
+
status: number,
|
|
259
|
+
notices: RefusalNotices
|
|
260
|
+
): string | null => {
|
|
261
|
+
if (status === 429) {
|
|
262
|
+
return notices.rateLimitMessage;
|
|
263
|
+
}
|
|
264
|
+
return status === 403 ? notices.verifyMessage : null;
|
|
265
|
+
};
|
|
266
|
+
|
|
202
267
|
/** The current route with the deployment base stripped, for page grounding. */
|
|
203
268
|
const currentPath = (): string =>
|
|
204
269
|
stripBase(import.meta.env.BASE_URL, window.location.pathname);
|
|
205
270
|
|
|
271
|
+
/**
|
|
272
|
+
* The conversation as the route is sent it: all of it while it fits the
|
|
273
|
+
* route's limits (`ai/ask-limits.ts`), else the latest turns that do, the
|
|
274
|
+
* oldest dropped first, so a long chat keeps working instead of answering
|
|
275
|
+
* `400`. The kept turns open with a question, not an answer cut from its
|
|
276
|
+
* own, and the question being asked always goes, even alone over the limit.
|
|
277
|
+
*/
|
|
278
|
+
const withinAskLimits = (history: AskMessage[]): AskMessage[] => {
|
|
279
|
+
const last = history.length - 1;
|
|
280
|
+
let start = Math.max(0, history.length - ASK_MAX_MESSAGES);
|
|
281
|
+
while (
|
|
282
|
+
start < last &&
|
|
283
|
+
JSON.stringify(history.slice(start)).length > ASK_MAX_MESSAGES_CHARS
|
|
284
|
+
) {
|
|
285
|
+
start += 1;
|
|
286
|
+
}
|
|
287
|
+
while (start < last && history[start]?.role !== "user") {
|
|
288
|
+
start += 1;
|
|
289
|
+
}
|
|
290
|
+
return history.slice(start);
|
|
291
|
+
};
|
|
292
|
+
|
|
206
293
|
/**
|
|
207
294
|
* Stream answers from the assistant endpoint. Mirrors the built-in assistant island so
|
|
208
295
|
* a custom chat UI shares the same grounded, page-aware backend.
|
|
@@ -212,8 +299,12 @@ export const useAssistant = (
|
|
|
212
299
|
): UseAssistant => {
|
|
213
300
|
const endpoint = options.endpoint ?? DEFAULT_ASK_ENDPOINT;
|
|
214
301
|
const errorMessage = options.errorMessage ?? ASK_ERROR;
|
|
302
|
+
const rateLimitMessage = options.rateLimitMessage ?? ASK_RATE_LIMITED;
|
|
303
|
+
const verifyMessage = options.verifyMessage ?? ASK_VERIFY_FAILED;
|
|
304
|
+
const { captcha } = options;
|
|
215
305
|
const [messages, setMessages] = useState<AskMessage[]>([]);
|
|
216
306
|
const [loading, setLoading] = useState(false);
|
|
307
|
+
const [thread, setThread] = useState(newThreadId);
|
|
217
308
|
// The stream writes into the conversation via state updates, so `reset()`
|
|
218
309
|
// mid-answer must revoke the in-flight request's right to write — otherwise
|
|
219
310
|
// its next chunk re-appends the assistant bubble onto the emptied list, and
|
|
@@ -255,7 +346,7 @@ export const useAssistant = (
|
|
|
255
346
|
) =>
|
|
256
347
|
track(
|
|
257
348
|
event,
|
|
258
|
-
{ ...props, path: pathname, questionChars: trimmed.length },
|
|
349
|
+
{ ...props, path: pathname, questionChars: trimmed.length, thread },
|
|
259
350
|
{ question: trimmed }
|
|
260
351
|
);
|
|
261
352
|
report("ask", {});
|
|
@@ -280,12 +371,32 @@ export const useAssistant = (
|
|
|
280
371
|
const assistant: AskMessage = { content: "", role: "assistant" };
|
|
281
372
|
setMessages([...history, assistant]);
|
|
282
373
|
setLoading(true);
|
|
374
|
+
// Answer with a notice instead: an error, or the route turning the
|
|
375
|
+
// question away. Silent once a reset has revoked the question.
|
|
376
|
+
const fail = (notice: string) => {
|
|
377
|
+
if (live()) {
|
|
378
|
+
outcome("ask_error", { status });
|
|
379
|
+
assistant.content = notice;
|
|
380
|
+
setMessages([...history, { ...assistant }]);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
283
383
|
try {
|
|
384
|
+
// A fresh bot-check token per question, when the site has a check;
|
|
385
|
+
// `null` when the check didn't pass in the browser.
|
|
386
|
+
const token = captcha
|
|
387
|
+
? await captchaToken(captcha).catch(() => null)
|
|
388
|
+
: undefined;
|
|
389
|
+
if (token === null) {
|
|
390
|
+
fail(verifyMessage);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const payload: AskPayload = {
|
|
394
|
+
captcha: token,
|
|
395
|
+
messages: withinAskLimits(history),
|
|
396
|
+
page: { path },
|
|
397
|
+
};
|
|
284
398
|
const response = await fetch(endpoint, {
|
|
285
|
-
body: JSON.stringify(
|
|
286
|
-
messages: history,
|
|
287
|
-
page: { path },
|
|
288
|
-
}),
|
|
399
|
+
body: JSON.stringify(payload),
|
|
289
400
|
headers: { "content-type": "application/json" },
|
|
290
401
|
method: "POST",
|
|
291
402
|
signal: controller.signal,
|
|
@@ -294,12 +405,12 @@ export const useAssistant = (
|
|
|
294
405
|
if (!response.ok) {
|
|
295
406
|
// An error body (JSON, HTML error page) must not stream in as the
|
|
296
407
|
// assistant's answer — only the route's own not-configured notice.
|
|
297
|
-
const notice =
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
408
|
+
const notice =
|
|
409
|
+
refusalNotice(status, {
|
|
410
|
+
rateLimitMessage,
|
|
411
|
+
verifyMessage,
|
|
412
|
+
}) ?? (await unavailableNotice(response).catch(() => null));
|
|
413
|
+
fail(notice ?? errorMessage);
|
|
303
414
|
return;
|
|
304
415
|
}
|
|
305
416
|
const reader = response.body?.getReader();
|
|
@@ -332,20 +443,14 @@ export const useAssistant = (
|
|
|
332
443
|
if (assistant.content) {
|
|
333
444
|
outcome("ask_answer", { chars: assistant.content.length });
|
|
334
445
|
} else {
|
|
335
|
-
|
|
336
|
-
assistant.content = errorMessage;
|
|
337
|
-
setMessages([...history, { ...assistant }]);
|
|
446
|
+
fail(errorMessage);
|
|
338
447
|
}
|
|
339
448
|
}
|
|
340
449
|
} catch {
|
|
341
450
|
// A thrown fetch (offline, DNS failure, CORS) must not strand the
|
|
342
451
|
// pre-appended empty assistant message as a stuck placeholder. A
|
|
343
452
|
// reset's abort lands here too — the guard keeps it silent.
|
|
344
|
-
|
|
345
|
-
outcome("ask_error", { status });
|
|
346
|
-
assistant.content = errorMessage;
|
|
347
|
-
setMessages([...history, { ...assistant }]);
|
|
348
|
-
}
|
|
453
|
+
fail(errorMessage);
|
|
349
454
|
// oxlint-disable-next-line react/todo -- React Compiler cannot lower try/finally; the hook stays manually memoized above
|
|
350
455
|
} finally {
|
|
351
456
|
if (live()) {
|
|
@@ -353,7 +458,16 @@ export const useAssistant = (
|
|
|
353
458
|
}
|
|
354
459
|
}
|
|
355
460
|
},
|
|
356
|
-
[
|
|
461
|
+
[
|
|
462
|
+
captcha,
|
|
463
|
+
endpoint,
|
|
464
|
+
errorMessage,
|
|
465
|
+
loading,
|
|
466
|
+
messages,
|
|
467
|
+
rateLimitMessage,
|
|
468
|
+
thread,
|
|
469
|
+
verifyMessage,
|
|
470
|
+
]
|
|
357
471
|
);
|
|
358
472
|
|
|
359
473
|
// Retained for the compiler-off opt-out path (`react: { compiler: false }`):
|
|
@@ -364,9 +478,10 @@ export const useAssistant = (
|
|
|
364
478
|
abortRef.current?.abort();
|
|
365
479
|
abortRef.current = null;
|
|
366
480
|
setMessages([]);
|
|
481
|
+
setThread(newThreadId());
|
|
367
482
|
// The in-flight `ask`'s finally is now stale and won't clear this.
|
|
368
483
|
setLoading(false);
|
|
369
484
|
}, []);
|
|
370
485
|
|
|
371
|
-
return { ask, loading, messages, reset };
|
|
486
|
+
return { ask, loading, messages, reset, thread };
|
|
372
487
|
};
|
|
@@ -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,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
|
}
|