blume 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -0
- package/dist/cli/{chunk-ga0pf4aj.js → chunk-0n9xkp3d.js} +12 -3
- package/dist/cli/{chunk-ga0pf4aj.js.map → chunk-0n9xkp3d.js.map} +3 -3
- package/dist/cli/{chunk-43zcf4gz.js → chunk-0pmb4tr4.js} +541 -368
- package/dist/cli/chunk-0pmb4tr4.js.map +35 -0
- package/dist/cli/{chunk-9geymqns.js → chunk-0w6hs479.js} +15 -15
- package/dist/cli/{chunk-xkz5xbtv.js → chunk-124fvmzq.js} +2802 -1422
- package/dist/cli/chunk-124fvmzq.js.map +220 -0
- package/dist/cli/chunk-150jk7km.js +52 -0
- package/dist/cli/chunk-150jk7km.js.map +10 -0
- package/dist/cli/{chunk-y4ykbark.js → chunk-1f882900.js} +2 -2
- package/dist/cli/{chunk-5ffywsmb.js → chunk-297ajdcv.js} +8 -6
- package/dist/cli/{chunk-5ffywsmb.js.map → chunk-297ajdcv.js.map} +3 -3
- package/dist/cli/{chunk-h2a48yhd.js → chunk-32khwcb6.js} +120 -16
- package/dist/cli/{chunk-h2a48yhd.js.map → chunk-32khwcb6.js.map} +10 -10
- package/dist/cli/{chunk-5cg2rzgf.js → chunk-36bxs9c7.js} +20 -49
- package/dist/cli/{chunk-5cg2rzgf.js.map → chunk-36bxs9c7.js.map} +4 -5
- package/dist/cli/{chunk-py62fewh.js → chunk-53jqw8z0.js} +3 -3
- package/dist/cli/{chunk-5wj2qwn2.js → chunk-58bt3hag.js} +4 -4
- package/dist/cli/{chunk-e7vvk5ya.js → chunk-5cmts6dv.js} +3 -3
- package/dist/cli/{chunk-y5h4r2y0.js → chunk-60hz265r.js} +15 -5
- package/dist/cli/chunk-60hz265r.js.map +10 -0
- package/dist/cli/chunk-74wfhv79.js +65 -0
- package/dist/cli/chunk-74wfhv79.js.map +10 -0
- package/dist/cli/{chunk-g2z9x01v.js → chunk-9qzgg6mj.js} +3 -3
- package/dist/cli/{chunk-q3wxxq39.js → chunk-a57yh0h9.js} +5 -5
- package/dist/cli/{chunk-713mngdq.js → chunk-ave1wcbr.js} +76 -10
- package/dist/cli/chunk-ave1wcbr.js.map +14 -0
- package/dist/cli/{chunk-2n4ps1b3.js → chunk-bqxaqn5p.js} +40 -22
- package/dist/cli/chunk-bqxaqn5p.js.map +15 -0
- package/dist/cli/{chunk-jnwj5kjj.js → chunk-bxwhtvw8.js} +20 -7
- package/dist/cli/{chunk-jnwj5kjj.js.map → chunk-bxwhtvw8.js.map} +3 -3
- package/dist/cli/chunk-ckrcm08h.js +324 -0
- package/dist/cli/chunk-ckrcm08h.js.map +14 -0
- package/dist/cli/{chunk-2f3ndkpw.js → chunk-desvm72z.js} +3 -3
- package/dist/cli/{chunk-gp84za0h.js → chunk-dhwtqnnd.js} +2 -2
- package/dist/cli/{chunk-4h8z9vv5.js → chunk-dpy8qdjf.js} +93 -47
- package/dist/cli/chunk-dpy8qdjf.js.map +17 -0
- package/dist/cli/{chunk-2f7n4qzt.js → chunk-dqhg54mr.js} +1 -1
- package/dist/cli/{chunk-0gz36akr.js → chunk-dv2z0rzf.js} +21 -5
- package/dist/cli/chunk-dv2z0rzf.js.map +12 -0
- package/dist/cli/{chunk-cm89scdv.js → chunk-es8h0v3j.js} +9 -11
- package/dist/cli/{chunk-cm89scdv.js.map → chunk-es8h0v3j.js.map} +3 -3
- package/dist/cli/{chunk-zhvd6adm.js → chunk-gfc5fqvf.js} +14 -14
- package/dist/cli/{chunk-nd1821sk.js → chunk-gxtg6ed2.js} +33 -19
- package/dist/cli/chunk-gxtg6ed2.js.map +10 -0
- package/dist/cli/{chunk-p804f3pe.js → chunk-hvcxng5f.js} +6 -6
- package/dist/cli/{chunk-ta27feev.js → chunk-j1x92mx9.js} +21 -2
- package/dist/cli/chunk-j1x92mx9.js.map +10 -0
- package/dist/cli/{chunk-rekqz4qg.js → chunk-k3gr1nm9.js} +19 -16
- package/dist/cli/chunk-k3gr1nm9.js.map +10 -0
- package/dist/cli/{chunk-1n228xqb.js → chunk-kdpzw38a.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js → chunk-ms9z66nz.js} +2 -2
- package/dist/cli/{chunk-cy5bm0j0.js.map → chunk-ms9z66nz.js.map} +2 -2
- package/dist/cli/{chunk-zf748avv.js → chunk-nbyvgnnm.js} +193 -286
- package/dist/cli/chunk-nbyvgnnm.js.map +36 -0
- package/dist/cli/{chunk-6bm0xpgd.js → chunk-s0bdnc7v.js} +2 -2
- package/dist/cli/{chunk-yxmk4pec.js → chunk-sc93apfs.js} +17 -16
- package/dist/cli/{chunk-yxmk4pec.js.map → chunk-sc93apfs.js.map} +3 -3
- package/dist/cli/{chunk-cmtcnh9f.js → chunk-tfycddbt.js} +2 -2
- package/dist/cli/{chunk-9xr4dgee.js → chunk-twy7psdz.js} +29 -16
- package/dist/cli/chunk-twy7psdz.js.map +10 -0
- package/dist/cli/{chunk-ntv4qrt7.js → chunk-y4msrwm9.js} +15 -15
- package/dist/cli/{chunk-yj17vs39.js → chunk-ydp3065p.js} +5 -5
- package/dist/cli/index.js +24 -19
- package/dist/cli/index.js.map +3 -3
- package/dist/types/ai/ask-context.d.ts +6 -2
- package/dist/types/ai/ask.d.ts +24 -5
- package/dist/types/ai/component-markdown.d.ts +2 -0
- package/dist/types/ai/openapi-components.d.ts +4 -1
- package/dist/types/analytics/clics.d.ts +37 -0
- package/dist/types/analytics/index.d.ts +2 -0
- package/dist/types/analytics/schema.d.ts +18 -0
- package/dist/types/astro/integration.d.ts +11 -4
- package/dist/types/astro/markdown-negotiation.d.ts +3 -0
- package/dist/types/astro/pages.d.ts +10 -0
- package/dist/types/cli/init/scaffold.d.ts +1 -1
- package/dist/types/components/content/api-field.d.ts +4 -1
- package/dist/types/components/content/api-page.d.ts +6 -1
- package/dist/types/components/openapi/code-samples.d.ts +29 -0
- package/dist/types/components/openapi/helpers.d.ts +22 -1
- package/dist/types/components/openapi/security.d.ts +10 -0
- package/dist/types/components/openapi/snippets.d.ts +60 -0
- package/dist/types/core/changelog-index.d.ts +8 -0
- package/dist/types/core/config-input.d.ts +37 -6
- package/dist/types/core/data.d.ts +4 -0
- package/dist/types/core/i18n-ui.d.ts +2 -0
- package/dist/types/core/links.d.ts +8 -0
- package/dist/types/core/powered-by.d.ts +12 -0
- package/dist/types/core/probe.d.ts +2 -0
- package/dist/types/core/project-graph.d.ts +6 -0
- package/dist/types/core/redirect-patterns.d.ts +11 -3
- package/dist/types/core/schema.d.ts +228 -3
- package/dist/types/core/sources/cache.d.ts +3 -0
- package/dist/types/core/sources/contentful-rich-text.d.ts +11 -2
- package/dist/types/core/sources/contentful.d.ts +17 -1
- package/dist/types/core/sources/lexical.d.ts +5 -0
- package/dist/types/core/sources/lower.d.ts +10 -2
- package/dist/types/core/sources/normalize.d.ts +20 -1
- package/dist/types/core/sources/notion.d.ts +8 -0
- package/dist/types/core/sources/remote.d.ts +37 -10
- package/dist/types/core/sources/sanity.d.ts +1 -1
- package/dist/types/core/types.d.ts +17 -0
- package/dist/types/deploy/adapter-output.d.ts +2 -1
- package/dist/types/deploy/adapters/cloudflare.d.ts +7 -5
- package/dist/types/deploy/adapters/node.d.ts +58 -23
- package/dist/types/deploy/adapters/registry.d.ts +10 -0
- package/dist/types/deploy/adapters/types.d.ts +3 -2
- package/dist/types/deploy/cloudflare-negotiation.d.ts +5 -2
- package/dist/types/deploy/node-headers.d.ts +12 -4
- package/dist/types/deploy/platforms/cloudflare.d.ts +10 -7
- package/dist/types/deploy/platforms/node.d.ts +3 -1
- package/dist/types/deploy/platforms/types.d.ts +18 -7
- package/dist/types/deploy/platforms/vercel.d.ts +21 -2
- package/dist/types/deploy/redirects.d.ts +17 -1
- package/dist/types/deploy/sitemap.d.ts +2 -1
- package/dist/types/deploy/vercel-negotiation.d.ts +44 -21
- package/dist/types/narration/build.d.ts +34 -12
- package/dist/types/narration/provider.d.ts +96 -8
- package/dist/types/openapi/checks.d.ts +27 -0
- package/dist/types/openapi/graphql.d.ts +6 -0
- package/dist/types/openapi/model.d.ts +15 -4
- package/dist/types/openapi/parse.d.ts +3 -0
- package/dist/types/openapi/references.d.ts +16 -0
- package/dist/types/openapi/sentence.d.ts +11 -1
- package/dist/types/ratelimit/index.d.ts +1 -1
- package/dist/types/ratelimit/schema.d.ts +8 -0
- package/dist/types/ratelimit/upstash.d.ts +39 -14
- package/dist/types/reference/graphql.d.ts +76 -1
- package/dist/types/reference/index.d.ts +1 -1
- package/dist/types/reference/openapi.d.ts +2 -2
- package/dist/types/reference/options.d.ts +37 -1
- package/dist/types/reference/schema.d.ts +84 -0
- package/dist/types/search/adapters/registry.d.ts +3 -0
- package/dist/types/search/adapters/typesense.d.ts +14 -4
- package/dist/types/search/build.d.ts +3 -1
- package/dist/types/search/orama-index.d.ts +9 -9
- package/dist/types/search/sync/algolia.d.ts +5 -2
- package/dist/types/search/sync/index.d.ts +3 -1
- package/dist/types/search/sync/typesense.d.ts +11 -7
- package/dist/types/seo/jsonld.d.ts +11 -6
- package/dist/types/sources/contentful.d.ts +16 -0
- package/dist/types/sources/github-releases.d.ts +8 -1
- package/dist/types/sources/notion.d.ts +8 -0
- package/dist/types/sources/payload.d.ts +2 -0
- package/dist/types/sources/registry.d.ts +63 -0
- package/dist/types/sources/shared.d.ts +3 -0
- package/dist/types/sources/strapi.d.ts +2 -0
- package/docs/01-quickstart.mdx +4 -2
- package/docs/02-deployment.mdx +59 -13
- package/docs/03-upgrading.mdx +2 -1
- package/docs/04-migrating.mdx +9 -9
- package/docs/08-faq.mdx +12 -12
- package/docs/advanced/changelog.mdx +30 -2
- package/docs/advanced/custom-pages.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +30 -7
- package/docs/cli/doctor.mdx +6 -2
- package/docs/cli/evals.mdx +4 -4
- package/docs/cli/translate.mdx +1 -0
- package/docs/cli/validate.mdx +4 -3
- package/docs/configuration/analytics.mdx +19 -1
- package/docs/configuration/assistant.mdx +6 -2
- package/docs/configuration/customization.mdx +4 -2
- package/docs/configuration/export.mdx +5 -3
- package/docs/configuration/index.mdx +4 -2
- package/docs/configuration/narration.mdx +43 -6
- package/docs/configuration/rate-limiting.mdx +23 -2
- package/docs/configuration/search.mdx +37 -14
- package/docs/content/components.mdx +2 -2
- package/docs/content/frontmatter.mdx +5 -3
- package/docs/content/i18n.mdx +2 -2
- package/docs/content/includes.mdx +1 -1
- package/docs/content/navigation.mdx +8 -6
- package/docs/content/sources/contentful.mdx +9 -4
- package/docs/content/sources/custom.mdx +7 -3
- package/docs/content/sources/github-releases.mdx +12 -2
- package/docs/content/sources/index.mdx +3 -3
- package/docs/content/sources/notion.mdx +17 -2
- package/docs/content/sources/obsidian.mdx +3 -1
- package/docs/content/sources/payload.mdx +5 -2
- package/docs/content/sources/remote-mdx.mdx +6 -0
- package/docs/content/sources/sanity.mdx +4 -2
- package/docs/content/sources/strapi.mdx +5 -2
- package/docs/content/syntax.mdx +2 -2
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +1 -1
- package/docs/discoverability/json-api.mdx +2 -0
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +3 -1
- package/docs/discoverability/mcp.mdx +6 -0
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/discoverability/structured-data.mdx +8 -6
- package/docs/references/api-pages.mdx +1 -1
- package/docs/references/asyncapi.mdx +5 -1
- package/docs/references/graphql.mdx +15 -1
- package/docs/references/openapi.mdx +64 -8
- package/package.json +9 -9
- package/skills/blume/SKILL.md +1 -1
- package/skills/blume-migrate/SKILL.md +2 -2
- package/skills/blume-migrate/assets/{oxfmt@0.67.0.patch → oxfmt@0.71.0.patch} +7 -7
- package/skills/blume-migrate/references/docusaurus.md +4 -4
- package/skills/blume-migrate/references/fumadocs.md +17 -13
- package/skills/blume-migrate/references/monorepo.md +5 -5
- package/skills/blume-migrate/references/nextra.md +8 -5
- package/skills/blume-migrate/references/starlight.md +9 -7
- package/src/ai/ask-context.ts +33 -3
- package/src/ai/ask-limits.ts +13 -0
- package/src/ai/ask.ts +24 -6
- package/src/ai/changelog-markdown.ts +11 -6
- package/src/ai/component-markdown.ts +1 -1
- package/src/ai/cors.ts +12 -3
- package/src/ai/mcp/query.ts +14 -6
- package/src/ai/openapi-components.ts +28 -4
- package/src/ai/relative-links.ts +4 -38
- package/src/analytics/clics.ts +58 -0
- package/src/analytics/head.ts +5 -0
- package/src/analytics/index.ts +2 -0
- package/src/analytics/schema.ts +2 -0
- package/src/astro/generate.ts +91 -56
- package/src/astro/integration.ts +72 -22
- package/src/astro/markdown-negotiation.ts +3 -0
- package/src/astro/module-types.ts +3 -1
- package/src/astro/pages.ts +23 -0
- package/src/astro/render-deps.ts +10 -5
- package/src/astro/runtime-deps.ts +23 -2
- package/src/astro/templates.ts +129 -81
- package/src/audit/catalog.ts +19 -3
- package/src/audit/checks/links.ts +14 -1
- package/src/audit/checks/network.ts +110 -6
- package/src/audit/checks/redirects.ts +6 -4
- package/src/audit/checks/robots.ts +58 -2
- package/src/audit/run.ts +3 -0
- package/src/audit/types.ts +2 -0
- package/src/audit/url.ts +14 -5
- package/src/blume-modules.d.ts +5 -1
- package/src/cli/args.ts +51 -0
- package/src/cli/build-failure.ts +23 -0
- package/src/cli/commands/audit.ts +10 -1
- package/src/cli/commands/dev.ts +18 -2
- package/src/cli/commands/doctor.ts +45 -12
- package/src/cli/commands/eval.ts +36 -4
- package/src/cli/commands/init.ts +6 -4
- package/src/cli/commands/validate.ts +10 -1
- package/src/cli/init/scaffold.ts +9 -6
- package/src/cli/prepare.ts +6 -3
- package/src/cli/required-secrets.ts +3 -2
- package/src/components/content/ApiPlayground.astro +6 -2
- package/src/components/content/api-field.ts +4 -1
- package/src/components/content/api-page.ts +31 -7
- package/src/components/content/mermaid-element.ts +15 -1
- package/src/components/content/view-switcher.ts +7 -4
- package/src/components/islands/hooks.ts +27 -1
- package/src/components/layout/NavTree.astro +49 -5
- package/src/components/layout/NavTreeScript.astro +18 -0
- package/src/components/layout/PageActions.astro +139 -24
- package/src/components/layout/PageLayout.astro +13 -3
- package/src/components/layout/RootLayout.astro +27 -14
- package/src/components/layout/Search.astro +17 -3
- package/src/components/layout/TableOfContents.astro +24 -17
- package/src/components/layout/analytics-client.ts +8 -8
- package/src/components/layout/head-scripts.ts +6 -1
- package/src/components/layout/search/algolia.ts +10 -1
- package/src/components/layout/search/analytics.ts +42 -14
- package/src/components/layout/search/endpoint.ts +48 -5
- package/src/components/layout/search/pagefind.ts +114 -5
- package/src/components/layout/search/types.ts +12 -0
- package/src/components/layout/search/typesense.ts +18 -6
- package/src/components/openapi/AsyncApiOperation.astro +14 -7
- package/src/components/openapi/Authorization.astro +10 -0
- package/src/components/openapi/GraphqlOperation.astro +5 -0
- package/src/components/openapi/Operation.astro +24 -1
- package/src/components/openapi/ParametersTable.astro +8 -4
- package/src/components/openapi/Playground.astro +7 -1
- package/src/components/openapi/RequestBody.astro +19 -2
- package/src/components/openapi/Responses.astro +1 -0
- package/src/components/openapi/SchemaTable.astro +15 -3
- package/src/components/openapi/async-snippets.ts +61 -5
- package/src/components/openapi/async.ts +123 -29
- package/src/components/openapi/graphql-helpers.ts +35 -12
- package/src/components/openapi/helpers.ts +36 -2
- package/src/components/openapi/message-model.ts +54 -11
- package/src/components/openapi/message.ts +14 -4
- package/src/components/openapi/operation-model.ts +16 -7
- package/src/components/openapi/playground-client.ts +77 -58
- package/src/components/openapi/playground-schema.ts +1 -1
- package/src/components/openapi/schema-tree.ts +44 -14
- package/src/components/openapi/security.ts +10 -0
- package/src/components/openapi/snippets.ts +5 -3
- package/src/components/openapi/validate-json.ts +4 -3
- package/src/components/powered-by-middleware.ts +33 -0
- package/src/core/changelog-index.ts +31 -1
- package/src/core/component-diagnostics.ts +53 -0
- package/src/core/component-overrides.ts +32 -0
- package/src/core/config-input.ts +42 -6
- package/src/core/data.ts +2 -0
- package/src/core/diagnostics.ts +5 -0
- package/src/core/i18n-ui.ts +2 -0
- package/src/core/includes.ts +1 -1
- package/src/core/links.ts +47 -17
- package/src/core/powered-by.ts +12 -0
- package/src/core/probe.ts +26 -6
- package/src/core/project-graph.ts +59 -18
- package/src/core/redirect-patterns.ts +36 -7
- package/src/core/schema.ts +26 -6
- package/src/core/sources/cache.ts +6 -0
- package/src/core/sources/contentful-rich-text.ts +29 -6
- package/src/core/sources/contentful.ts +135 -25
- package/src/core/sources/filesystem.ts +26 -4
- package/src/core/sources/github-releases.ts +12 -1
- package/src/core/sources/lexical.ts +15 -10
- package/src/core/sources/lower.ts +19 -3
- package/src/core/sources/mdx-remote.ts +62 -10
- package/src/core/sources/normalize.ts +227 -41
- package/src/core/sources/notion.ts +34 -10
- package/src/core/sources/obsidian.ts +16 -3
- package/src/core/sources/payload.ts +38 -15
- package/src/core/sources/remote.ts +72 -15
- package/src/core/sources/resolve.ts +11 -1
- package/src/core/sources/sanity.ts +43 -25
- package/src/core/sources/strapi.ts +2 -2
- package/src/core/types.ts +18 -0
- package/src/core/ui-packs/ar.ts +1 -0
- package/src/core/ui-packs/bg.ts +1 -0
- package/src/core/ui-packs/bn.ts +1 -0
- package/src/core/ui-packs/ca.ts +1 -0
- package/src/core/ui-packs/cs.ts +1 -0
- package/src/core/ui-packs/da.ts +1 -0
- package/src/core/ui-packs/de.ts +1 -0
- package/src/core/ui-packs/el.ts +1 -0
- package/src/core/ui-packs/es.ts +1 -0
- package/src/core/ui-packs/fa.ts +1 -0
- package/src/core/ui-packs/fi.ts +1 -0
- package/src/core/ui-packs/fr.ts +1 -0
- package/src/core/ui-packs/he.ts +1 -0
- package/src/core/ui-packs/hi.ts +1 -0
- package/src/core/ui-packs/hr.ts +1 -0
- package/src/core/ui-packs/hu.ts +1 -0
- package/src/core/ui-packs/id.ts +1 -0
- package/src/core/ui-packs/it.ts +1 -0
- package/src/core/ui-packs/ja.ts +1 -0
- package/src/core/ui-packs/ko.ts +1 -0
- package/src/core/ui-packs/nl.ts +1 -0
- package/src/core/ui-packs/no.ts +1 -0
- package/src/core/ui-packs/pl.ts +1 -0
- package/src/core/ui-packs/pt-br.ts +1 -0
- package/src/core/ui-packs/pt.ts +1 -0
- package/src/core/ui-packs/ro.ts +1 -0
- package/src/core/ui-packs/ru.ts +1 -0
- package/src/core/ui-packs/sk.ts +1 -0
- package/src/core/ui-packs/sr.ts +1 -0
- package/src/core/ui-packs/sv.ts +1 -0
- package/src/core/ui-packs/th.ts +1 -0
- package/src/core/ui-packs/tr.ts +1 -0
- package/src/core/ui-packs/uk.ts +1 -0
- package/src/core/ui-packs/vi.ts +1 -0
- package/src/core/ui-packs/zh-tw.ts +1 -0
- package/src/core/ui-packs/zh.ts +1 -0
- package/src/deploy/adapter-output.ts +7 -2
- package/src/deploy/adapters/cloudflare.ts +7 -5
- package/src/deploy/adapters/node.ts +47 -9
- package/src/deploy/adapters/types.ts +7 -3
- package/src/deploy/artifacts.ts +2 -1
- package/src/deploy/cloudflare-negotiation.ts +36 -7
- package/src/deploy/headers.ts +15 -1
- package/src/deploy/node-headers.ts +32 -20
- package/src/deploy/platforms/cloudflare.ts +14 -9
- package/src/deploy/platforms/netlify.ts +2 -1
- package/src/deploy/platforms/node.ts +12 -2
- package/src/deploy/platforms/types.ts +21 -7
- package/src/deploy/platforms/vercel.ts +115 -17
- package/src/deploy/redirects.ts +66 -6
- package/src/deploy/rss.ts +3 -1
- package/src/deploy/sitemap.ts +39 -5
- package/src/deploy/vercel-negotiation.ts +325 -103
- package/src/eval/agents.ts +18 -1
- package/src/eval/prompts.ts +6 -3
- package/src/eval/report.ts +34 -13
- package/src/eval/run.ts +5 -0
- package/src/markdown/base-links.ts +15 -14
- package/src/markdown/package-commands.ts +9 -8
- package/src/markdown/relative-links.ts +3 -2
- package/src/narration/build.ts +218 -40
- package/src/narration/provider.ts +89 -9
- package/src/openapi/checks.ts +273 -0
- package/src/openapi/graphql-build.ts +21 -0
- package/src/openapi/graphql.ts +11 -0
- package/src/openapi/model.ts +15 -3
- package/src/openapi/parse.ts +127 -2
- package/src/openapi/references.ts +43 -3
- package/src/openapi/render-mdx.ts +68 -8
- package/src/openapi/sentence.ts +29 -2
- package/src/openapi/source.ts +39 -14
- package/src/ratelimit/index.ts +1 -1
- package/src/ratelimit/runtime.ts +37 -19
- package/src/ratelimit/upstash.ts +40 -10
- package/src/reference/graphql.ts +15 -1
- package/src/reference/index.ts +1 -0
- package/src/reference/openapi.ts +2 -2
- package/src/reference/options.ts +29 -1
- package/src/registry/eject.ts +8 -2
- package/src/search/adapters/locale-scope.ts +30 -0
- package/src/search/adapters/typesense.ts +14 -4
- package/src/search/build.ts +21 -3
- package/src/search/orama-index.ts +163 -31
- package/src/search/source-pages.ts +29 -0
- package/src/search/sync/algolia.ts +99 -9
- package/src/search/sync/index.ts +52 -13
- package/src/search/sync/typesense.ts +72 -27
- package/src/seo/jsonld.ts +38 -20
- package/src/sources/contentful.ts +12 -0
- package/src/sources/github-releases.ts +7 -1
- package/src/sources/notion.ts +7 -0
- package/src/sources/shared.ts +3 -0
- package/src/theme/color.ts +9 -10
- package/src/theme/entry.ts +43 -6
- package/src/translate/validate.ts +23 -1
- package/dist/cli/chunk-0gz36akr.js.map +0 -12
- package/dist/cli/chunk-2n4ps1b3.js.map +0 -15
- package/dist/cli/chunk-43zcf4gz.js.map +0 -36
- package/dist/cli/chunk-4ejxbzee.js +0 -39
- package/dist/cli/chunk-4ejxbzee.js.map +0 -10
- package/dist/cli/chunk-4h8z9vv5.js.map +0 -16
- package/dist/cli/chunk-713mngdq.js.map +0 -14
- package/dist/cli/chunk-9xr4dgee.js.map +0 -10
- package/dist/cli/chunk-amrb6hgh.js +0 -145
- package/dist/cli/chunk-amrb6hgh.js.map +0 -11
- package/dist/cli/chunk-nd1821sk.js.map +0 -10
- package/dist/cli/chunk-rekqz4qg.js.map +0 -10
- package/dist/cli/chunk-ta27feev.js.map +0 -10
- package/dist/cli/chunk-xkz5xbtv.js.map +0 -213
- package/dist/cli/chunk-y5h4r2y0.js.map +0 -10
- package/dist/cli/chunk-zf748avv.js.map +0 -37
- /package/dist/cli/{chunk-9geymqns.js.map → chunk-0w6hs479.js.map} +0 -0
- /package/dist/cli/{chunk-y4ykbark.js.map → chunk-1f882900.js.map} +0 -0
- /package/dist/cli/{chunk-py62fewh.js.map → chunk-53jqw8z0.js.map} +0 -0
- /package/dist/cli/{chunk-5wj2qwn2.js.map → chunk-58bt3hag.js.map} +0 -0
- /package/dist/cli/{chunk-e7vvk5ya.js.map → chunk-5cmts6dv.js.map} +0 -0
- /package/dist/cli/{chunk-g2z9x01v.js.map → chunk-9qzgg6mj.js.map} +0 -0
- /package/dist/cli/{chunk-q3wxxq39.js.map → chunk-a57yh0h9.js.map} +0 -0
- /package/dist/cli/{chunk-2f3ndkpw.js.map → chunk-desvm72z.js.map} +0 -0
- /package/dist/cli/{chunk-gp84za0h.js.map → chunk-dhwtqnnd.js.map} +0 -0
- /package/dist/cli/{chunk-2f7n4qzt.js.map → chunk-dqhg54mr.js.map} +0 -0
- /package/dist/cli/{chunk-zhvd6adm.js.map → chunk-gfc5fqvf.js.map} +0 -0
- /package/dist/cli/{chunk-p804f3pe.js.map → chunk-hvcxng5f.js.map} +0 -0
- /package/dist/cli/{chunk-1n228xqb.js.map → chunk-kdpzw38a.js.map} +0 -0
- /package/dist/cli/{chunk-6bm0xpgd.js.map → chunk-s0bdnc7v.js.map} +0 -0
- /package/dist/cli/{chunk-cmtcnh9f.js.map → chunk-tfycddbt.js.map} +0 -0
- /package/dist/cli/{chunk-ntv4qrt7.js.map → chunk-y4msrwm9.js.map} +0 -0
- /package/dist/cli/{chunk-yj17vs39.js.map → chunk-ydp3065p.js.map} +0 -0
package/src/cli/commands/eval.ts
CHANGED
|
@@ -104,12 +104,37 @@ const parseFlags = (args: EvalFlags) => {
|
|
|
104
104
|
return { agent: args.agent, threshold, timeoutS };
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
+
/** A shell word for the rerun command, single-quoted only when it has to be. */
|
|
108
|
+
const shellWord = (value: string): string =>
|
|
109
|
+
/^[\w./:@%+=,-]+$/u.test(value)
|
|
110
|
+
? value
|
|
111
|
+
: `'${value.replaceAll("'", String.raw`'\''`)}'`;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The command a `--fix` agent reruns to verify its edits. It carries the run's
|
|
115
|
+
* own flags: a bare `blume eval` would fall back to Codex and `evals.yaml`,
|
|
116
|
+
* grading a different file with a different agent than the run it's fixing.
|
|
117
|
+
*/
|
|
118
|
+
const rerunCommand = (
|
|
119
|
+
args: EvalFlags,
|
|
120
|
+
{ agent, threshold, timeoutS }: ReturnType<typeof parseFlags>
|
|
121
|
+
): string =>
|
|
122
|
+
[
|
|
123
|
+
`blume eval --agent ${agent}`,
|
|
124
|
+
args.file === DEFAULT_FILE ? "" : `--file ${shellWord(args.file)}`,
|
|
125
|
+
args.threshold === undefined ? "" : `--threshold ${threshold}`,
|
|
126
|
+
args.timeout === undefined ? "" : `--timeout ${timeoutS}`,
|
|
127
|
+
]
|
|
128
|
+
.filter((part) => part !== "")
|
|
129
|
+
.join(" ");
|
|
130
|
+
|
|
107
131
|
/** `blume eval --fix`: hand the failing report to the interactive agent. */
|
|
108
132
|
const runFixHandoff = async (
|
|
109
133
|
agent: AgentKind,
|
|
110
134
|
result: EvalResult,
|
|
111
135
|
root: string,
|
|
112
|
-
threshold: number
|
|
136
|
+
threshold: number,
|
|
137
|
+
command: string
|
|
113
138
|
): Promise<void> => {
|
|
114
139
|
const count = result.counts.fail + result.counts.error;
|
|
115
140
|
if (count === 0) {
|
|
@@ -120,7 +145,7 @@ const runFixHandoff = async (
|
|
|
120
145
|
process.stderr.write(
|
|
121
146
|
` Handing ${count} failed question${count === 1 ? "" : "s"} to ${cli.name}…\n\n`
|
|
122
147
|
);
|
|
123
|
-
const code = await launchAgentCode(agent, evalFixPrompt(report));
|
|
148
|
+
const code = await launchAgentCode(agent, evalFixPrompt(report, command));
|
|
124
149
|
if (code !== 0) {
|
|
125
150
|
process.exit(code);
|
|
126
151
|
}
|
|
@@ -184,7 +209,8 @@ export const evalCommand = defineCommand({
|
|
|
184
209
|
meta: commandMeta.eval,
|
|
185
210
|
async run({ args }) {
|
|
186
211
|
const root = process.cwd();
|
|
187
|
-
const
|
|
212
|
+
const flags = parseFlags(args);
|
|
213
|
+
const { agent, threshold, timeoutS } = flags;
|
|
188
214
|
if (args.action === "init") {
|
|
189
215
|
await runInit(agent, args.file);
|
|
190
216
|
return;
|
|
@@ -259,7 +285,13 @@ export const evalCommand = defineCommand({
|
|
|
259
285
|
if (args.fix) {
|
|
260
286
|
// The gate is a CI concern; a handoff run succeeds when the agent
|
|
261
287
|
// session does, not when the docs already passed.
|
|
262
|
-
await runFixHandoff(
|
|
288
|
+
await runFixHandoff(
|
|
289
|
+
agent,
|
|
290
|
+
result,
|
|
291
|
+
root,
|
|
292
|
+
threshold,
|
|
293
|
+
rerunCommand(args, flags)
|
|
294
|
+
);
|
|
263
295
|
return;
|
|
264
296
|
}
|
|
265
297
|
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -272,14 +272,16 @@ export const initCommand = defineCommand({
|
|
|
272
272
|
// which `init` leaves alone; say what it still needs before installing.
|
|
273
273
|
const note = workspaceNote(root, answers, env);
|
|
274
274
|
|
|
275
|
-
// Keep installed dependencies, Blume's generated runtime (`.blume/`),
|
|
276
|
-
// build output (`dist/`)
|
|
277
|
-
//
|
|
278
|
-
//
|
|
275
|
+
// Keep installed dependencies, Blume's generated runtime (`.blume/`),
|
|
276
|
+
// build output (`dist/`), and local secrets (`.env.local`, where every
|
|
277
|
+
// missing-secret warning says to put them) out of version control.
|
|
278
|
+
// Idempotent: creates `.gitignore` when absent and skips entries already
|
|
279
|
+
// present (trailing-slash agnostic).
|
|
279
280
|
const ignored = await ensureGitignore(root, [
|
|
280
281
|
"node_modules/",
|
|
281
282
|
".blume/",
|
|
282
283
|
"dist/",
|
|
284
|
+
".env.local",
|
|
283
285
|
]);
|
|
284
286
|
if (ignored.length > 0) {
|
|
285
287
|
sink.success(`Added ${ignored.join(", ")} to .gitignore`);
|
|
@@ -13,6 +13,7 @@ import { validateLinks } from "../../core/links.ts";
|
|
|
13
13
|
import { buildManifest } from "../../core/manifest.ts";
|
|
14
14
|
import { scanProject } from "../../core/project-graph.ts";
|
|
15
15
|
import type { Diagnostic } from "../../core/types.ts";
|
|
16
|
+
import { parseIgnoreFlag } from "../args.ts";
|
|
16
17
|
import { commandMeta } from "../command-meta.ts";
|
|
17
18
|
import { reportInternalError } from "../internal-error.ts";
|
|
18
19
|
import {
|
|
@@ -28,6 +29,12 @@ export const validateCommand = defineCommand({
|
|
|
28
29
|
description: "Check external (HTTP) links over the network.",
|
|
29
30
|
type: "boolean",
|
|
30
31
|
},
|
|
32
|
+
ignore: {
|
|
33
|
+
description:
|
|
34
|
+
'With --external, skip external links whose URL matches this glob (e.g. "https://example.com/**"). Repeat it for more; internal links are always checked.',
|
|
35
|
+
type: "string",
|
|
36
|
+
valueHint: "glob",
|
|
37
|
+
},
|
|
31
38
|
json: {
|
|
32
39
|
description: "Emit diagnostics as JSON on stdout (for CI/editors).",
|
|
33
40
|
type: "boolean",
|
|
@@ -38,8 +45,9 @@ export const validateCommand = defineCommand({
|
|
|
38
45
|
},
|
|
39
46
|
},
|
|
40
47
|
meta: commandMeta.validate,
|
|
41
|
-
async run({ args }) {
|
|
48
|
+
async run({ args, rawArgs }) {
|
|
42
49
|
const root = process.cwd();
|
|
50
|
+
const ignore = parseIgnoreFlag(rawArgs);
|
|
43
51
|
const diagnostics: Diagnostic[] = [];
|
|
44
52
|
|
|
45
53
|
try {
|
|
@@ -83,6 +91,7 @@ export const validateCommand = defineCommand({
|
|
|
83
91
|
checkExternal: Boolean(args.external),
|
|
84
92
|
extraRoutes,
|
|
85
93
|
i18n: project.config.i18n,
|
|
94
|
+
ignore,
|
|
86
95
|
publicDir: existsSync(publicDir) ? publicDir : null,
|
|
87
96
|
redirects: project.config.redirects,
|
|
88
97
|
}))
|
package/src/cli/init/scaffold.ts
CHANGED
|
@@ -557,6 +557,8 @@ const pnpmWorkspaceSchema = z.looseObject({
|
|
|
557
557
|
// A glob's leading `./` and trailing `/`, which pnpm ignores.
|
|
558
558
|
const GLOB_EDGES = /^\.\/|\/+$/gu;
|
|
559
559
|
|
|
560
|
+
const globPattern = (glob: string): string => glob.replaceAll(GLOB_EDGES, "");
|
|
561
|
+
|
|
560
562
|
/** Whether any of `globs` matches the workspace-relative path `at`. */
|
|
561
563
|
const matchesAny = (globs: string[], at: string): boolean =>
|
|
562
564
|
globs.length > 0 && picomatch(globs)(at);
|
|
@@ -589,11 +591,12 @@ const listsPackage = (workspace: string, root: string): boolean => {
|
|
|
589
591
|
return true;
|
|
590
592
|
}
|
|
591
593
|
const globs = parsed.data.packages;
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
+
const included = globs
|
|
595
|
+
.filter((glob) => !glob.startsWith("!"))
|
|
596
|
+
.map(globPattern);
|
|
594
597
|
const excluded = globs
|
|
595
598
|
.filter((glob) => glob.startsWith("!"))
|
|
596
|
-
.map((glob) =>
|
|
599
|
+
.map((glob) => globPattern(glob.slice(1)));
|
|
597
600
|
return matchesAny(included, at) && !matchesAny(excluded, at);
|
|
598
601
|
};
|
|
599
602
|
|
|
@@ -679,7 +682,7 @@ const ESBUILD_APPROVAL = " allowBuilds:\n esbuild: true";
|
|
|
679
682
|
* What the project still needs from a workspace it sits in, when `init` left
|
|
680
683
|
* that workspace's config alone: a place in a pnpm workspace's `packages`
|
|
681
684
|
* (pnpm skips a folder it doesn't list), pnpm's approval for esbuild's build
|
|
682
|
-
* script (pnpm
|
|
685
|
+
* script (pnpm 11 and later fail the install without it), or Yarn Berry's
|
|
683
686
|
* `node_modules` linker. Undefined when the workspace already has it, or the
|
|
684
687
|
* project doesn't sit in one.
|
|
685
688
|
*/
|
|
@@ -699,12 +702,12 @@ export const workspaceNote = (
|
|
|
699
702
|
const entry = ` packages:\n - ${relative(workspace, root)}`;
|
|
700
703
|
return approved
|
|
701
704
|
? `This folder sits inside the pnpm workspace at ${file}, which doesn't list it under packages, so pnpm won't install it. Add it there before installing:\n\n${entry}`
|
|
702
|
-
: `This folder sits inside the pnpm workspace at ${file}, which doesn't list it under packages, so pnpm won't install it. Add it there before installing, and approve esbuild's build script, or pnpm
|
|
705
|
+
: `This folder sits inside the pnpm workspace at ${file}, which doesn't list it under packages, so pnpm won't install it. Add it there before installing, and approve esbuild's build script, or pnpm 11 and later stop the install:\n\n${entry}\n${ESBUILD_APPROVAL}`;
|
|
703
706
|
}
|
|
704
707
|
if (approved) {
|
|
705
708
|
return;
|
|
706
709
|
}
|
|
707
|
-
return `This project joins the pnpm workspace at ${file}. Approve esbuild's build script there before installing, or pnpm
|
|
710
|
+
return `This project joins the pnpm workspace at ${file}. Approve esbuild's build script there before installing, or pnpm 11 and later stop the install:\n\n${ESBUILD_APPROVAL}`;
|
|
708
711
|
}
|
|
709
712
|
if (answers.packageManager !== "yarn" || yarnMajor(env.userAgent) === 1) {
|
|
710
713
|
return;
|
package/src/cli/prepare.ts
CHANGED
|
@@ -59,6 +59,12 @@ export const prepareProject = async (
|
|
|
59
59
|
let project: BlumeProject;
|
|
60
60
|
try {
|
|
61
61
|
project = await scanProject(options.root, {
|
|
62
|
+
// Fail-fast on missing secrets: warn now, not at the first request, and
|
|
63
|
+
// before the sources fetch, so a source that can't authenticate on a
|
|
64
|
+
// fresh build still gets its variable named.
|
|
65
|
+
beforeSources: (config) => {
|
|
66
|
+
reportDiagnostics(checkRequiredSecrets(config), options.root);
|
|
67
|
+
},
|
|
62
68
|
devServerUrl: options.devServerUrl,
|
|
63
69
|
mode: options.mode,
|
|
64
70
|
overrides: options.overrides,
|
|
@@ -138,8 +144,5 @@ export const prepareProject = async (
|
|
|
138
144
|
logger.warn(warning);
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
// Fail-fast on missing runtime secrets: warn now, not at the first request.
|
|
142
|
-
reportDiagnostics(checkRequiredSecrets(project.config), options.root);
|
|
143
|
-
|
|
144
147
|
return project;
|
|
145
148
|
};
|
|
@@ -61,7 +61,7 @@ const adapterSecretOwners = (config: ResolvedConfig): SecretOwner[] => {
|
|
|
61
61
|
* the failure surfaces at `blume dev`/`build` instead of at the first request in
|
|
62
62
|
* production. These are runtime secrets (the endpoint reads them on the server),
|
|
63
63
|
* so this warns rather than hard-fails — the value may live only in the deploy
|
|
64
|
-
* environment. Build-time secrets (search-index sync)
|
|
64
|
+
* environment. Build-time secrets (search-index sync) are checked by the sync.
|
|
65
65
|
*/
|
|
66
66
|
export const checkRequiredSecrets = (config: ResolvedConfig): Diagnostic[] => {
|
|
67
67
|
const diagnostics: Diagnostic[] = [];
|
|
@@ -92,7 +92,8 @@ export const checkRequiredSecrets = (config: ResolvedConfig): Diagnostic[] => {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// Generated narration reads its key at build, not at runtime; without it the
|
|
95
|
-
// build skips the audio and pages read with browser voices.
|
|
95
|
+
// build skips the audio and pages read with browser voices. `openai()` with
|
|
96
|
+
// a `baseUrl` requires only a key it names (see `narrationProviderSchema`).
|
|
96
97
|
const { narration } = config;
|
|
97
98
|
if (narration.enabled && narration.provider) {
|
|
98
99
|
for (const env of narration.provider.requiredSecrets) {
|
|
@@ -11,7 +11,10 @@ import { operationModel } from "../openapi/operation-model.ts";
|
|
|
11
11
|
import Playground from "../openapi/Playground.astro";
|
|
12
12
|
import { buildRequest, defaultValues } from "../openapi/request.ts";
|
|
13
13
|
import RequestPanel from "../openapi/RequestPanel.astro";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_SAMPLE_LANGUAGES,
|
|
16
|
+
sampleLanguages,
|
|
17
|
+
} from "../openapi/snippets.ts";
|
|
15
18
|
import {
|
|
16
19
|
endpointBody,
|
|
17
20
|
endpointParameters,
|
|
@@ -46,6 +49,7 @@ const tryIt = spec.playground !== "simple" && api.playground.enabled;
|
|
|
46
49
|
model={model}
|
|
47
50
|
operation={`${spec.method} ${spec.target}`}
|
|
48
51
|
proxy={api.playground.proxy}
|
|
52
|
+
proxyConfig="api"
|
|
49
53
|
slug="api-pages"
|
|
50
54
|
/>
|
|
51
55
|
)
|
|
@@ -53,7 +57,7 @@ const tryIt = spec.playground !== "simple" && api.playground.enabled;
|
|
|
53
57
|
{
|
|
54
58
|
!spec.requestExample && (
|
|
55
59
|
<RequestPanel
|
|
56
|
-
languages={sampleLanguages(
|
|
60
|
+
languages={sampleLanguages(DEFAULT_SAMPLE_LANGUAGES)}
|
|
57
61
|
responses={{}}
|
|
58
62
|
sample={buildRequest(model, defaultValues(model))}
|
|
59
63
|
schemas={{}}
|
|
@@ -43,7 +43,10 @@ export interface ParamFieldProps extends FieldProps {
|
|
|
43
43
|
/** The name, for a field with no location attribute. */
|
|
44
44
|
name?: string;
|
|
45
45
|
path?: string;
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* An example value: the one the playground and request samples start
|
|
48
|
+
* with, not an input's placeholder hint (see `fieldSchema`).
|
|
49
|
+
*/
|
|
47
50
|
placeholder?: string;
|
|
48
51
|
query?: string;
|
|
49
52
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { exampleValue } from "../openapi/helpers.ts";
|
|
1
2
|
import type { ParameterLike, SchemaLike } from "../openapi/helpers.ts";
|
|
2
3
|
import { resolveSecurity } from "../openapi/security.ts";
|
|
3
4
|
import type { OperationSecurity } from "../openapi/security.ts";
|
|
@@ -168,19 +169,42 @@ export const endpointParameters = (fields: EndpointField[]): ParameterLike[] =>
|
|
|
168
169
|
]
|
|
169
170
|
);
|
|
170
171
|
|
|
171
|
-
/**
|
|
172
|
+
/** Whether a field, or one nested in it, has a `default` or `placeholder`. */
|
|
173
|
+
const hasValue = (field: EndpointField): boolean =>
|
|
174
|
+
field.default !== undefined ||
|
|
175
|
+
Boolean(field.placeholder) ||
|
|
176
|
+
(field.fields ?? []).some(hasValue);
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The fields a request starts with: required ones, and ones with a value to
|
|
180
|
+
* send. An optional field with neither is left for the reader to fill,
|
|
181
|
+
* rather than sent as the sampler's `"string"`, `0`, or `true`.
|
|
182
|
+
*/
|
|
183
|
+
const sentFields = (fields: EndpointField[]): EndpointField[] =>
|
|
184
|
+
fields
|
|
185
|
+
.filter((field) => field.required === true || hasValue(field))
|
|
186
|
+
.map((field) => ({ ...field, fields: sentFields(field.fields ?? []) }));
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The body fields as a JSON request body, or `undefined` when there are none.
|
|
190
|
+
* Its example, which the playground and samples start from, holds only the
|
|
191
|
+
* {@link sentFields}.
|
|
192
|
+
*/
|
|
172
193
|
export const endpointBody = (fields: EndpointField[]) => {
|
|
173
194
|
const body = fields.filter((field) => field.location === "body");
|
|
195
|
+
const object = (members: EndpointField[]): SchemaLike =>
|
|
196
|
+
fieldSchema({
|
|
197
|
+
fields: members,
|
|
198
|
+
location: "body",
|
|
199
|
+
name: "",
|
|
200
|
+
type: "object",
|
|
201
|
+
});
|
|
174
202
|
return body.length > 0
|
|
175
203
|
? {
|
|
176
204
|
content: {
|
|
177
205
|
"application/json": {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
location: "body",
|
|
181
|
-
name: "",
|
|
182
|
-
type: "object",
|
|
183
|
-
}),
|
|
206
|
+
example: exampleValue(object(sentFields(body)), {}),
|
|
207
|
+
schema: object(body),
|
|
184
208
|
},
|
|
185
209
|
},
|
|
186
210
|
}
|
|
@@ -55,6 +55,9 @@ export class BlumeMermaid extends HTMLElement {
|
|
|
55
55
|
look: "classic",
|
|
56
56
|
securityLevel: "strict",
|
|
57
57
|
startOnLoad: false,
|
|
58
|
+
// Without this, a diagram that fails to parse leaves Mermaid's own
|
|
59
|
+
// "Syntax error in text" graphic appended to <body>, below the page.
|
|
60
|
+
suppressErrorRendering: true,
|
|
58
61
|
theme: prefersDark() ? "dark" : "default",
|
|
59
62
|
});
|
|
60
63
|
try {
|
|
@@ -71,13 +74,24 @@ export class BlumeMermaid extends HTMLElement {
|
|
|
71
74
|
// oxlint-disable-next-line github/no-inner-html -- Mermaid-generated SVG must be injected as HTML
|
|
72
75
|
output.innerHTML = svg;
|
|
73
76
|
}
|
|
74
|
-
} catch {
|
|
77
|
+
} catch (error) {
|
|
75
78
|
// Localized message stamped on <body> by RootLayout's markup (the
|
|
76
79
|
// data-attribute channel); English fallback when the attribute is
|
|
77
80
|
// missing (a stale snapshot or a custom layout).
|
|
78
81
|
output.textContent =
|
|
79
82
|
document.body.dataset.i18nDiagramError ||
|
|
80
83
|
"Could not render this diagram.";
|
|
84
|
+
// Readers get only the message above; the writer needs Mermaid's
|
|
85
|
+
// parser error (line and expected token) to fix the source. It goes
|
|
86
|
+
// to the console, and `blume dev` also shows it under the message.
|
|
87
|
+
console.error("[blume] Mermaid could not render a diagram:", error);
|
|
88
|
+
if (import.meta.env.DEV) {
|
|
89
|
+
const detail = document.createElement("pre");
|
|
90
|
+
detail.className =
|
|
91
|
+
"mt-2 overflow-x-auto text-muted-foreground text-xs";
|
|
92
|
+
detail.textContent = String(error);
|
|
93
|
+
output.append(detail);
|
|
94
|
+
}
|
|
81
95
|
}
|
|
82
96
|
output.removeAttribute("aria-busy");
|
|
83
97
|
};
|
|
@@ -42,6 +42,10 @@ const cssString = (value: string): string =>
|
|
|
42
42
|
.replaceAll("<", String.raw`\3c `)
|
|
43
43
|
.replaceAll(/[\n\r\f]/gu, " ")}"`;
|
|
44
44
|
|
|
45
|
+
/** Everything tagged with a view other than `title`. */
|
|
46
|
+
const otherViews = (title: string): string =>
|
|
47
|
+
`[data-blume-view]:not([data-blume-view=${cssString(title)}])`;
|
|
48
|
+
|
|
45
49
|
/**
|
|
46
50
|
* The page's view CSS: with a view chosen on `<body>`, everything tagged with
|
|
47
51
|
* another is hidden; before a choice, everything but the first view is.
|
|
@@ -51,12 +55,11 @@ export const viewStyle = (titles: readonly string[]): string => {
|
|
|
51
55
|
if (first === undefined) {
|
|
52
56
|
return "";
|
|
53
57
|
}
|
|
54
|
-
const other = (title: string): string =>
|
|
55
|
-
`[data-blume-view]:not([data-blume-view=${cssString(title)}])`;
|
|
56
58
|
const selectors = [
|
|
57
|
-
`body:not([data-blume-view]) ${
|
|
59
|
+
`body:not([data-blume-view]) ${otherViews(first)}`,
|
|
58
60
|
...titles.map(
|
|
59
|
-
(title) =>
|
|
61
|
+
(title) =>
|
|
62
|
+
`body[data-blume-view=${cssString(title)}] ${otherViews(title)}`
|
|
60
63
|
),
|
|
61
64
|
];
|
|
62
65
|
return `${selectors.join(",")}{display:none!important}`;
|
|
@@ -1,5 +1,9 @@
|
|
|
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";
|
|
3
7
|
import { captchaToken } from "../../captcha/client.ts";
|
|
4
8
|
import type { CaptchaSettings } from "../../captcha/schema.ts";
|
|
5
9
|
import type { BlumeClientData } from "../../core/data.ts";
|
|
@@ -264,6 +268,28 @@ const refusalNotice = (
|
|
|
264
268
|
const currentPath = (): string =>
|
|
265
269
|
stripBase(import.meta.env.BASE_URL, window.location.pathname);
|
|
266
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
|
+
|
|
267
293
|
/**
|
|
268
294
|
* Stream answers from the assistant endpoint. Mirrors the built-in assistant island so
|
|
269
295
|
* a custom chat UI shares the same grounded, page-aware backend.
|
|
@@ -366,7 +392,7 @@ export const useAssistant = (
|
|
|
366
392
|
}
|
|
367
393
|
const payload: AskPayload = {
|
|
368
394
|
captcha: token,
|
|
369
|
-
messages: history,
|
|
395
|
+
messages: withinAskLimits(history),
|
|
370
396
|
page: { path },
|
|
371
397
|
};
|
|
372
398
|
const response = await fetch(endpoint, {
|
|
@@ -307,15 +307,59 @@ const initialId =
|
|
|
307
307
|
const spacing =
|
|
308
308
|
index === 0 ? "mt-0" : display === "flat" ? "mt-6" : "mt-0.5";
|
|
309
309
|
|
|
310
|
-
// `page`: a single row that drills into this group's panel.
|
|
311
|
-
// controls that panel; NavTreeScript keeps `aria-expanded` in
|
|
312
|
-
// with the panel shown (static markup, since cached subtrees and
|
|
313
|
-
// fragments can't carry per-page state).
|
|
310
|
+
// `page`: a single row that drills into this group's panel. Its drill
|
|
311
|
+
// button controls that panel; NavTreeScript keeps `aria-expanded` in
|
|
312
|
+
// step with the panel shown (static markup, since cached subtrees and
|
|
313
|
+
// fragments can't carry per-page state). A group with an index page
|
|
314
|
+
// splits the row, like the `group` summary: the label links that page
|
|
315
|
+
// (which renders with the panel open), so the row opens the section
|
|
316
|
+
// even when the index hides its own row, and the chevron stays a
|
|
317
|
+
// button that slides the panel in without leaving the page. Without
|
|
318
|
+
// one, the whole row is the drill button, so it never links a missing
|
|
319
|
+
// page. `aria-current` marks the row whose panel holds the current
|
|
320
|
+
// page (`page` on the section's own page), so the parent panel still
|
|
321
|
+
// shows where the reader is after the back arrow. Only rows on the
|
|
322
|
+
// active path carry it, and those always render live, never from a
|
|
323
|
+
// cache.
|
|
314
324
|
if (display === "page") {
|
|
315
|
-
|
|
325
|
+
const current = hasActivePage(item)
|
|
326
|
+
? isGroupRowCurrent(item, currentRoute)
|
|
327
|
+
? "page"
|
|
328
|
+
: "true"
|
|
329
|
+
: undefined;
|
|
330
|
+
return item.route ? (
|
|
331
|
+
<li class={`flex items-center gap-0.5 ${spacing}`}>
|
|
332
|
+
<a
|
|
333
|
+
aria-current={current}
|
|
334
|
+
class="blume-nav-drill-link"
|
|
335
|
+
href={withMountedBase(item.route)}
|
|
336
|
+
>
|
|
337
|
+
{item.icon && (
|
|
338
|
+
<Icon
|
|
339
|
+
class="shrink-0 text-muted-foreground"
|
|
340
|
+
name={item.icon}
|
|
341
|
+
size={14}
|
|
342
|
+
/>
|
|
343
|
+
)}
|
|
344
|
+
<span class="flex-1 truncate">{item.label}</span>
|
|
345
|
+
{item.badge && <span class={badgeClassFor(item.badge)}>{item.badge}</span>}
|
|
346
|
+
</a>
|
|
347
|
+
<button
|
|
348
|
+
aria-controls={navPanelId(id)}
|
|
349
|
+
aria-expanded="false"
|
|
350
|
+
aria-label={item.label}
|
|
351
|
+
class="blume-nav-drill-toggle"
|
|
352
|
+
data-nav-to={id}
|
|
353
|
+
type="button"
|
|
354
|
+
>
|
|
355
|
+
<Icon class="rtl:-scale-x-100" name="chevron-right" size={14} />
|
|
356
|
+
</button>
|
|
357
|
+
</li>
|
|
358
|
+
) : (
|
|
316
359
|
<li class={spacing}>
|
|
317
360
|
<button
|
|
318
361
|
aria-controls={navPanelId(id)}
|
|
362
|
+
aria-current={current}
|
|
319
363
|
aria-expanded="false"
|
|
320
364
|
class="blume-nav-drill"
|
|
321
365
|
data-nav-to={id}
|
|
@@ -146,6 +146,24 @@
|
|
|
146
146
|
}
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
|
+
// After the back arrow, the current section's name links the page
|
|
150
|
+
// already on screen, so a plain click slides its panel back in rather
|
|
151
|
+
// than reloading that page. Modified clicks stay the browser's.
|
|
152
|
+
const own = target.closest('.blume-nav-drill-link[aria-current="page"]');
|
|
153
|
+
const ownId = own?.parentElement
|
|
154
|
+
?.querySelector("[data-nav-to]")
|
|
155
|
+
?.getAttribute("data-nav-to");
|
|
156
|
+
if (
|
|
157
|
+
own &&
|
|
158
|
+
ownId &&
|
|
159
|
+
this.contains(own) &&
|
|
160
|
+
this.panel(ownId) &&
|
|
161
|
+
!(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
|
|
162
|
+
) {
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
void this.show(ownId, true);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
149
167
|
const back = target.closest("[data-nav-back]");
|
|
150
168
|
if (back && this.contains(back)) {
|
|
151
169
|
event.preventDefault();
|