blume 1.7.3 → 2.0.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 +19 -0
- package/CHANGELOG.md +245 -0
- package/README.md +34 -21
- package/dist/cli/{chunk-vacwm2hv.js → chunk-27g6wdth.js} +2 -2
- package/dist/cli/{chunk-ps4m1xh4.js → chunk-2hn4b8z7.js} +513 -550
- package/dist/cli/chunk-2hn4b8z7.js.map +12 -0
- package/dist/cli/chunk-5shv93fd.js +39 -0
- package/dist/cli/chunk-5shv93fd.js.map +10 -0
- package/dist/cli/chunk-6crbhc3x.js +361 -0
- package/dist/cli/chunk-6crbhc3x.js.map +14 -0
- package/dist/cli/{chunk-8cjtbafj.js → chunk-6hsn950k.js} +109 -383
- package/dist/cli/chunk-6hsn950k.js.map +10 -0
- package/dist/cli/chunk-6vm74dry.js +148 -0
- package/dist/cli/chunk-6vm74dry.js.map +10 -0
- package/dist/cli/{chunk-yg63d42r.js → chunk-79jhk4py.js} +87 -58
- package/dist/cli/chunk-79jhk4py.js.map +35 -0
- package/dist/cli/chunk-82bbrxdn.js +51 -0
- package/dist/cli/chunk-82bbrxdn.js.map +10 -0
- package/dist/cli/chunk-abh8yjkn.js +31 -0
- package/dist/cli/chunk-abh8yjkn.js.map +10 -0
- package/dist/cli/chunk-ah61y8py.js +75 -0
- package/dist/cli/chunk-ah61y8py.js.map +11 -0
- package/dist/cli/{chunk-dwgcp5sm.js → chunk-ce574jw2.js} +1 -1
- package/dist/cli/chunk-ch6g3ar0.js +102 -0
- package/dist/cli/chunk-ch6g3ar0.js.map +10 -0
- package/dist/cli/chunk-dh8cwk36.js +279 -0
- package/dist/cli/chunk-dh8cwk36.js.map +10 -0
- package/dist/cli/{chunk-rqy0s5wh.js → chunk-epjnccmv.js} +17 -16
- package/dist/cli/{chunk-rqy0s5wh.js.map → chunk-epjnccmv.js.map} +3 -3
- package/dist/cli/chunk-f2z5v128.js +97 -0
- package/dist/cli/chunk-f2z5v128.js.map +10 -0
- package/dist/cli/{chunk-2z47ypj8.js → chunk-fa25z98p.js} +16 -3
- package/dist/cli/chunk-fa25z98p.js.map +11 -0
- package/dist/cli/{chunk-1jefwnfs.js → chunk-fs23ddbb.js} +1076 -2592
- package/dist/cli/chunk-fs23ddbb.js.map +35 -0
- package/dist/cli/{chunk-e7f42gdj.js → chunk-fxypxtvm.js} +2 -2
- package/dist/cli/{chunk-bvwwhd84.js → chunk-fz5wtpmh.js} +15 -15
- package/dist/cli/{chunk-bvwwhd84.js.map → chunk-fz5wtpmh.js.map} +1 -1
- package/dist/cli/chunk-hdpx1tax.js +91 -0
- package/dist/cli/chunk-hdpx1tax.js.map +10 -0
- package/dist/cli/{chunk-ct47dqpx.js → chunk-jts8mvcz.js} +67 -7
- package/dist/cli/{chunk-2mzebbbz.js.map → chunk-jts8mvcz.js.map} +6 -4
- package/dist/cli/{chunk-ahnw3kxw.js → chunk-jwyddg7y.js} +27 -22
- package/dist/cli/chunk-jwyddg7y.js.map +15 -0
- package/dist/cli/{chunk-cjtn640a.js → chunk-kdp5q7ke.js} +43 -20
- package/dist/cli/chunk-kdp5q7ke.js.map +10 -0
- package/dist/cli/{chunk-5g0w1e2c.js → chunk-kpf8rrjc.js} +28 -16
- package/dist/cli/{chunk-5g0w1e2c.js.map → chunk-kpf8rrjc.js.map} +4 -4
- package/dist/cli/chunk-m3vmjgmq.js +133 -0
- package/dist/cli/chunk-m3vmjgmq.js.map +10 -0
- package/dist/cli/{chunk-b27xqwn9.js → chunk-mb2919y2.js} +9 -5
- package/dist/cli/chunk-mb2919y2.js.map +10 -0
- package/dist/cli/{chunk-5qk08vmp.js → chunk-q5163e60.js} +133 -54
- package/dist/cli/chunk-q5163e60.js.map +11 -0
- package/dist/cli/chunk-qkqwkpte.js +12437 -0
- package/dist/cli/chunk-qkqwkpte.js.map +182 -0
- package/dist/cli/{chunk-4x36ddpw.js → chunk-qs4q5p4e.js} +81 -87
- package/dist/cli/chunk-qs4q5p4e.js.map +10 -0
- package/dist/cli/{chunk-0xjyb285.js → chunk-qwsrynx5.js} +15 -5
- package/dist/cli/{chunk-0xjyb285.js.map → chunk-qwsrynx5.js.map} +4 -4
- package/dist/cli/chunk-s1p84fyh.js +261 -0
- package/dist/cli/chunk-s1p84fyh.js.map +10 -0
- package/dist/cli/chunk-s6jhgk0q.js +176 -0
- package/dist/cli/chunk-s6jhgk0q.js.map +11 -0
- package/dist/cli/chunk-vtk4a6dg.js +374 -0
- package/dist/cli/chunk-vtk4a6dg.js.map +10 -0
- package/dist/cli/{chunk-k79xp7av.js → chunk-wgm7m9qk.js} +230 -700
- package/dist/cli/chunk-wgm7m9qk.js.map +36 -0
- package/dist/cli/chunk-wm7js3j9.js +145 -0
- package/dist/cli/chunk-wm7js3j9.js.map +11 -0
- package/dist/cli/chunk-yt5n7ppj.js +79 -0
- package/dist/cli/chunk-yt5n7ppj.js.map +10 -0
- package/dist/cli/{chunk-3r45185y.js → chunk-yw7dm696.js} +9 -11
- package/dist/cli/{chunk-3r45185y.js.map → chunk-yw7dm696.js.map} +3 -3
- package/dist/cli/{chunk-nn13znc2.js → chunk-zxcczpyx.js} +1 -1
- package/dist/cli/chunk-zxh4d9vy.js +122 -0
- package/dist/cli/chunk-zxh4d9vy.js.map +11 -0
- package/dist/cli/index.js +214 -57
- package/dist/cli/index.js.map +8 -7
- package/dist/types/ai/agent-readability.d.ts +52 -0
- package/dist/types/ai/ai-catalog.d.ts +42 -0
- package/dist/types/ai/api/paths.d.ts +17 -0
- package/dist/types/ai/api-catalog.d.ts +18 -0
- package/dist/types/ai/ask-context.d.ts +7 -7
- package/dist/types/ai/ask.d.ts +368 -0
- package/dist/types/ai/changelog-markdown.d.ts +2 -0
- package/dist/types/ai/component-markdown.d.ts +2 -2
- package/dist/types/ai/index.d.ts +24 -0
- package/dist/types/ai/link-headers.d.ts +24 -0
- package/dist/types/ai/llms.d.ts +25 -0
- package/dist/types/ai/markdown.d.ts +45 -0
- package/dist/types/ai/mcp/discovery.d.ts +68 -0
- package/dist/types/ai/mcp/tools.d.ts +16 -0
- package/dist/types/ai/openapi-components.d.ts +43 -0
- package/dist/types/ai/relative-links.d.ts +26 -0
- package/dist/types/ai/serializers.d.ts +15 -0
- package/dist/types/ai/skills.d.ts +42 -0
- package/dist/types/ai/tar.d.ts +25 -0
- package/dist/types/ai/visibility.d.ts +17 -0
- package/dist/types/ai/web-bot-auth.d.ts +16 -0
- package/dist/types/analytics/adobe.d.ts +35 -0
- package/dist/types/analytics/amplitude.d.ts +50 -0
- package/dist/types/analytics/clarity.d.ts +31 -0
- package/dist/types/analytics/clearbit.d.ts +29 -0
- package/dist/types/analytics/cloudflare.d.ts +41 -0
- package/dist/types/analytics/fathom.d.ts +41 -0
- package/dist/types/analytics/google-analytics.d.ts +44 -0
- package/dist/types/analytics/google-tag-manager.d.ts +40 -0
- package/dist/types/analytics/head.d.ts +30 -0
- package/dist/types/analytics/heap.d.ts +40 -0
- package/dist/types/analytics/hightouch.d.ts +44 -0
- package/dist/types/analytics/hotjar.d.ts +33 -0
- package/dist/types/analytics/index.d.ts +61 -0
- package/dist/types/analytics/inline.d.ts +17 -0
- package/dist/types/analytics/logrocket.d.ts +44 -0
- package/dist/types/analytics/mixpanel.d.ts +67 -0
- package/dist/types/analytics/pirsch.d.ts +42 -0
- package/dist/types/analytics/plausible.d.ts +56 -0
- package/dist/types/analytics/posthog.d.ts +45 -0
- package/dist/types/analytics/schema.d.ts +320 -0
- package/dist/types/analytics/script.d.ts +46 -0
- package/dist/types/analytics/segment.d.ts +50 -0
- package/dist/types/analytics/vercel.d.ts +48 -0
- package/dist/types/astro/integration.d.ts +76 -0
- package/dist/types/astro/markdown-negotiation.d.ts +23 -0
- package/dist/types/astro/module-types.d.ts +14 -0
- package/dist/types/astro/pages.d.ts +44 -0
- package/dist/types/cli/env.d.ts +12 -0
- package/dist/types/cli/init/scaffold.d.ts +154 -0
- package/dist/types/components/layout/nav-utils.d.ts +11 -0
- package/dist/types/core/adapter.d.ts +47 -0
- package/dist/types/core/api-name.d.ts +7 -0
- package/dist/types/core/changelog-index.d.ts +13 -0
- package/dist/types/core/config-input.d.ts +197 -526
- package/dist/types/core/config.d.ts +61 -35
- package/dist/types/core/content-assets.d.ts +76 -0
- package/dist/types/core/custom-pages.d.ts +33 -0
- package/dist/types/core/data.d.ts +30 -15
- package/dist/types/core/define-components.d.ts +12 -9
- package/dist/types/core/deployment-env.d.ts +6 -11
- package/dist/types/core/frontmatter.d.ts +10 -0
- package/dist/types/core/graph.d.ts +18 -0
- package/dist/types/core/heading-markers.d.ts +54 -0
- package/dist/types/core/i18n-ui.d.ts +12 -10
- package/dist/types/core/i18n.d.ts +87 -0
- package/dist/types/core/includes.d.ts +138 -0
- package/dist/types/core/last-modified.d.ts +47 -0
- package/dist/types/core/links.d.ts +95 -0
- package/dist/types/core/locale-links.d.ts +60 -0
- package/dist/types/core/manifest.d.ts +17 -0
- package/dist/types/core/meta.d.ts +38 -0
- package/dist/types/core/nav-diagnostics.d.ts +26 -0
- package/dist/types/core/navigation.d.ts +6 -0
- package/dist/types/core/node-require.d.ts +19 -0
- package/dist/types/core/package-json.d.ts +13 -0
- package/dist/types/core/probe.d.ts +42 -0
- package/dist/types/core/project-graph.d.ts +51 -0
- package/dist/types/core/project.d.ts +2 -0
- package/dist/types/core/safe-href.d.ts +2 -0
- package/dist/types/core/safe-links.d.ts +26 -0
- package/dist/types/core/schema.d.ts +2284 -454
- package/dist/types/core/site-url.d.ts +16 -0
- package/dist/types/core/sources/assets.d.ts +36 -0
- package/dist/types/core/sources/cache.d.ts +37 -0
- package/dist/types/core/sources/collection.d.ts +28 -0
- package/dist/types/core/sources/contentful-rich-text.d.ts +31 -0
- package/dist/types/core/sources/contentful.d.ts +40 -0
- package/dist/types/core/sources/filesystem.d.ts +24 -0
- package/dist/types/core/sources/github-releases.d.ts +31 -0
- package/dist/types/core/sources/json.d.ts +30 -0
- package/dist/types/core/sources/lexical.d.ts +19 -0
- package/dist/types/core/sources/lower.d.ts +75 -0
- package/dist/types/core/sources/mdx-remote.d.ts +28 -0
- package/dist/types/core/sources/normalize.d.ts +147 -0
- package/dist/types/core/sources/notion.d.ts +131 -0
- package/dist/types/core/sources/obsidian.d.ts +46 -0
- package/dist/types/core/sources/payload.d.ts +39 -0
- package/dist/types/core/sources/portable-text.d.ts +42 -0
- package/dist/types/core/sources/read.d.ts +23 -0
- package/dist/types/core/sources/remote.d.ts +74 -0
- package/dist/types/core/sources/resolve.d.ts +19 -0
- package/dist/types/core/sources/sanity.d.ts +43 -0
- package/dist/types/core/sources/strapi-blocks.d.ts +12 -0
- package/dist/types/core/sources/strapi.d.ts +33 -0
- package/dist/types/core/sources/types.d.ts +7 -0
- package/dist/types/core/sources/watch.d.ts +45 -0
- package/dist/types/core/text-width.d.ts +11 -0
- package/dist/types/core/types.d.ts +15 -1
- package/dist/types/core/unrecognized-keys.d.ts +7 -0
- package/dist/types/core/versions.d.ts +72 -0
- package/dist/types/core/yaml.d.ts +9 -0
- package/dist/types/deploy/adapter-output.d.ts +45 -0
- package/dist/types/deploy/adapters/cloudflare.d.ts +40 -0
- package/dist/types/deploy/adapters/index.d.ts +29 -0
- package/dist/types/deploy/adapters/netlify.d.ts +37 -0
- package/dist/types/deploy/adapters/node.d.ts +37 -0
- package/dist/types/deploy/adapters/registry.d.ts +133 -0
- package/dist/types/deploy/adapters/types.d.ts +71 -0
- package/dist/types/deploy/adapters/vercel.d.ts +38 -0
- package/dist/types/deploy/artifacts.d.ts +65 -0
- package/dist/types/deploy/cloudflare-negotiation.d.ts +196 -0
- package/dist/types/deploy/function-bundle.d.ts +80 -0
- package/dist/types/deploy/headers.d.ts +50 -0
- package/dist/types/deploy/node-headers.d.ts +42 -0
- package/dist/types/deploy/platforms/cloudflare.d.ts +40 -0
- package/dist/types/deploy/platforms/index.d.ts +18 -0
- package/dist/types/deploy/platforms/netlify.d.ts +13 -0
- package/dist/types/deploy/platforms/node.d.ts +11 -0
- package/dist/types/deploy/platforms/paths.d.ts +27 -0
- package/dist/types/deploy/platforms/static.d.ts +10 -0
- package/dist/types/deploy/platforms/types.d.ts +104 -0
- package/dist/types/deploy/platforms/vercel.d.ts +32 -0
- package/dist/types/deploy/redirects.d.ts +53 -0
- package/dist/types/deploy/robots.d.ts +8 -0
- package/dist/types/deploy/rss.d.ts +31 -0
- package/dist/types/deploy/sitemap.d.ts +21 -0
- package/dist/types/deploy/vercel-negotiation.d.ts +109 -0
- package/dist/types/markdown/code-title.d.ts +32 -0
- package/dist/types/markdown/fence-meta.d.ts +23 -0
- package/dist/types/markdown/themes.d.ts +3 -3
- package/dist/types/openapi/asyncapi.d.ts +129 -0
- package/dist/types/openapi/graphql-build.d.ts +8 -0
- package/dist/types/openapi/graphql.d.ts +122 -0
- package/dist/types/openapi/model.d.ts +158 -0
- package/dist/types/openapi/parse.d.ts +57 -0
- package/dist/types/openapi/references.d.ts +37 -25
- package/dist/types/openapi/render-mdx.d.ts +33 -0
- package/dist/types/openapi/sentence.d.ts +7 -0
- package/dist/types/openapi/signature.d.ts +10 -0
- package/dist/types/openapi/source.d.ts +22 -0
- package/dist/types/openapi/spec-dependency-error.d.ts +10 -0
- package/dist/types/reference/asyncapi.d.ts +166 -0
- package/dist/types/reference/graphql.d.ts +181 -0
- package/dist/types/reference/index.d.ts +32 -0
- package/dist/types/reference/openapi.d.ts +165 -0
- package/dist/types/reference/options.d.ts +157 -0
- package/dist/types/reference/scalar.d.ts +136 -0
- package/dist/types/reference/schema.d.ts +630 -0
- package/dist/types/search/adapters/algolia.d.ts +32 -0
- package/dist/types/search/adapters/flexsearch.d.ts +12 -0
- package/dist/types/search/adapters/index.d.ts +31 -0
- package/dist/types/search/adapters/mixedbread.d.ts +22 -0
- package/dist/types/search/adapters/orama-cloud.d.ts +33 -0
- package/dist/types/search/adapters/orama.d.ts +13 -0
- package/dist/types/search/adapters/pagefind.d.ts +12 -0
- package/dist/types/search/adapters/registry.d.ts +228 -0
- package/dist/types/search/adapters/types.d.ts +34 -0
- package/dist/types/search/adapters/typesense.d.ts +42 -0
- package/dist/types/search/build.d.ts +23 -0
- package/dist/types/search/documents.d.ts +90 -0
- package/dist/types/search/facets.d.ts +3 -0
- package/dist/types/search/orama-index.d.ts +1 -1
- package/dist/types/search/sync/algolia.d.ts +14 -0
- package/dist/types/search/sync/index.d.ts +14 -0
- package/dist/types/search/sync/orama-cloud.d.ts +10 -0
- package/dist/types/search/sync/typesense.d.ts +14 -0
- package/dist/types/sources/contentful.d.ts +68 -0
- package/dist/types/sources/custom.d.ts +20 -0
- package/dist/types/sources/filesystem.d.ts +42 -0
- package/dist/types/sources/github-releases.d.ts +46 -0
- package/dist/types/sources/index.d.ts +48 -0
- package/dist/types/sources/mdx-remote.d.ts +63 -0
- package/dist/types/sources/notion.d.ts +65 -0
- package/dist/types/sources/obsidian.d.ts +34 -0
- package/dist/types/sources/payload.d.ts +68 -0
- package/dist/types/sources/registry.d.ts +1414 -0
- package/dist/types/sources/sanity.d.ts +69 -0
- package/dist/types/sources/shared.d.ts +46 -0
- package/dist/types/sources/strapi.d.ts +66 -0
- package/dist/types/theme/icon-kind.d.ts +11 -0
- package/dist/types/theme/icons.d.ts +20 -0
- package/docs/01-quickstart.mdx +18 -18
- package/docs/02-deployment.mdx +52 -28
- package/docs/03-upgrading.mdx +364 -0
- package/docs/04-migrating.mdx +58 -0
- package/docs/08-faq.mdx +5 -5
- package/docs/advanced/blog.mdx +13 -6
- package/docs/advanced/changelog.mdx +23 -35
- package/docs/advanced/custom-pages.mdx +22 -10
- package/docs/advanced/meta.ts +1 -8
- package/docs/advanced/skills.mdx +8 -0
- package/docs/cli/audit.mdx +646 -0
- package/docs/cli/doctor.mdx +29 -0
- package/docs/{reference/eval.mdx → cli/evals.mdx} +12 -11
- package/docs/cli/index.mdx +105 -0
- package/docs/cli/meta.ts +7 -0
- package/docs/{reference → cli}/translate.mdx +9 -9
- package/docs/cli/validate.mdx +41 -0
- package/docs/cli/version.mdx +40 -0
- package/docs/configuration/analytics.mdx +350 -59
- package/docs/configuration/assistant.mdx +357 -0
- package/docs/configuration/customization.mdx +16 -10
- package/docs/configuration/index.mdx +50 -31
- package/docs/configuration/meta.ts +1 -1
- package/docs/configuration/search.mdx +76 -55
- package/docs/configuration/theming.mdx +24 -15
- package/docs/content/components.mdx +21 -5
- package/docs/{reference → content}/frontmatter.mdx +35 -1
- package/docs/content/i18n.mdx +9 -7
- package/docs/content/includes.mdx +2 -4
- package/docs/content/index.mdx +1 -1
- package/docs/content/islands.mdx +11 -6
- package/docs/content/meta.mdx +1 -1
- package/docs/content/meta.ts +1 -0
- package/docs/content/navigation.mdx +9 -7
- package/docs/content/sources.mdx +145 -50
- package/docs/content/syntax.mdx +16 -12
- package/docs/content/versioning.mdx +3 -3
- package/docs/discoverability/agent-discovery.mdx +12 -12
- package/docs/discoverability/index.mdx +4 -4
- package/docs/discoverability/json-api.mdx +4 -4
- package/docs/discoverability/llms-txt.mdx +6 -6
- package/docs/discoverability/markdown.mdx +4 -4
- package/docs/discoverability/mcp.mdx +11 -11
- package/docs/discoverability/sitemap-and-robots.mdx +3 -3
- package/docs/index.mdx +4 -4
- package/docs/references/asyncapi.mdx +59 -0
- package/docs/{advanced → references}/graphql.mdx +45 -32
- package/docs/{reference → references}/meta.ts +2 -2
- package/docs/references/openapi.mdx +171 -0
- package/docs/references/scalar.mdx +64 -0
- package/package.json +42 -8
- package/skills/blume/SKILL.md +23 -10
- package/skills/blume-migrate/SKILL.md +22 -21
- package/skills/blume-migrate/assets/oxfmt@0.67.0.patch +49 -0
- package/skills/blume-migrate/references/docusaurus.md +5 -4
- package/skills/blume-migrate/references/fumadocs.md +4 -4
- package/skills/blume-migrate/references/mintlify.md +23 -8
- package/skills/blume-migrate/references/monorepo.md +6 -6
- package/skills/blume-migrate/references/starlight.md +4 -4
- package/src/ai/agent-readability.ts +25 -26
- package/src/ai/ai-catalog.ts +50 -32
- package/src/ai/api/handlers.ts +46 -11
- package/src/ai/api/paths.ts +1 -1
- package/src/ai/api-catalog.ts +8 -8
- package/src/ai/ask-context.ts +7 -7
- package/src/ai/ask-data.ts +3 -3
- package/src/ai/ask.ts +637 -100
- package/src/ai/changelog-markdown.ts +91 -0
- package/src/ai/component-markdown.ts +328 -12
- package/src/ai/cors.ts +3 -3
- package/src/ai/index.ts +45 -0
- package/src/ai/link-headers.ts +7 -6
- package/src/ai/llms.ts +20 -15
- package/src/ai/markdown.ts +35 -5
- package/src/ai/mcp/data.ts +9 -5
- package/src/ai/openapi-components.ts +5 -2
- package/src/ai/relative-links.ts +170 -0
- package/src/ai/serializers.ts +3 -3
- package/src/ai/skills.ts +1 -1
- package/src/ai/visibility.ts +1 -1
- package/src/ai/web-bot-auth.ts +2 -2
- package/src/analytics/adobe.ts +46 -0
- package/src/analytics/amplitude.ts +79 -0
- package/src/analytics/clarity.ts +46 -0
- package/src/analytics/clearbit.ts +47 -0
- package/src/analytics/cloudflare.ts +67 -0
- package/src/analytics/fathom.ts +64 -0
- package/src/analytics/google-analytics.ts +84 -0
- package/src/analytics/google-tag-manager.ts +61 -0
- package/src/analytics/head.ts +130 -0
- package/src/analytics/heap.ts +65 -0
- package/src/analytics/hightouch.ts +77 -0
- package/src/analytics/hotjar.ts +47 -0
- package/src/analytics/index.ts +71 -0
- package/src/analytics/inline.ts +21 -0
- package/src/analytics/logrocket.ts +71 -0
- package/src/analytics/mixpanel.ts +92 -0
- package/src/analytics/pirsch.ts +66 -0
- package/src/analytics/plausible.ts +83 -0
- package/src/analytics/posthog.ts +78 -0
- package/src/analytics/schema.ts +60 -0
- package/src/analytics/script.ts +60 -0
- package/src/analytics/segment.ts +85 -0
- package/src/analytics/vercel.ts +50 -0
- package/src/astro/adapter-root.ts +7 -9
- package/src/astro/component-slots.ts +131 -91
- package/src/astro/generate.ts +129 -361
- package/src/astro/integration.ts +2 -6
- package/src/astro/module-types.ts +1 -1
- package/src/astro/pages.ts +13 -101
- package/src/astro/render-deps.ts +379 -0
- package/src/astro/runtime-deps.ts +201 -0
- package/src/astro/templates.ts +575 -562
- package/src/audit/agent.ts +24 -0
- package/src/audit/catalog.ts +2 -2
- package/src/audit/checks/assets.ts +2 -2
- package/src/audit/checks/dns-aid.ts +1 -1
- package/src/audit/checks/duplicates.ts +3 -1
- package/src/audit/checks/i18n.ts +1 -1
- package/src/audit/checks/indexability.ts +7 -7
- package/src/audit/checks/links.ts +2 -2
- package/src/audit/checks/llms.ts +6 -6
- package/src/audit/checks/network.ts +3 -3
- package/src/audit/checks/og-image.ts +2 -2
- package/src/audit/checks/robots.ts +1 -1
- package/src/audit/checks/sitemap.ts +2 -2
- package/src/audit/checks/social.ts +1 -1
- package/src/audit/run.ts +4 -3
- package/src/audit/terms.ts +31 -0
- package/src/audit/url.ts +13 -13
- package/src/blume-modules.d.ts +2 -2
- package/src/cli/command-meta.ts +10 -0
- package/src/cli/commands/audit.ts +40 -21
- package/src/cli/commands/build.ts +71 -316
- package/src/cli/commands/check.ts +1 -0
- package/src/cli/commands/dev.ts +40 -1
- package/src/cli/commands/doctor.ts +92 -14
- package/src/cli/commands/eject.ts +44 -7
- package/src/cli/commands/eval.ts +5 -5
- package/src/cli/commands/init.ts +161 -41
- package/src/cli/commands/migrate.ts +121 -0
- package/src/cli/commands/preview.ts +7 -1
- package/src/cli/commands/translate.ts +5 -5
- package/src/cli/commands/upgrade.ts +141 -0
- package/src/cli/commands/version.ts +57 -44
- package/src/cli/eject-scripts.ts +121 -6
- package/src/cli/index.ts +11 -1
- package/src/cli/init/install.ts +70 -0
- package/src/cli/init/questions.ts +7 -0
- package/src/cli/init/scaffold.ts +459 -75
- package/src/cli/lazy-command.ts +37 -1
- package/src/cli/prepare.ts +40 -6
- package/src/cli/required-secrets.ts +39 -12
- package/src/cli/unknown-flags.ts +266 -0
- package/src/cli/yarn-pnp.ts +52 -0
- package/src/components/content/AccordionItem.astro +7 -1
- package/src/components/content/Card.astro +4 -3
- package/src/components/content/ColorItem.astro +22 -4
- package/src/components/content/GithubInfo.astro +2 -2
- package/src/components/content/Prompt.astro +25 -25
- package/src/components/content/Tabs.astro +3 -1
- package/src/components/content/Tile.astro +2 -3
- package/src/components/content/Tooltip.astro +57 -9
- package/src/components/content/content-strings.ts +34 -0
- package/src/components/content/diff.ts +1 -1
- package/src/components/content/mermaid-element.ts +13 -2
- package/src/components/content/prompt-markdown.ts +292 -0
- package/src/components/content/tooltip-id.ts +41 -0
- package/src/components/copy-feedback.ts +1 -1
- package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
- package/src/components/islands/{ask-ai.tsx → assistant.tsx} +23 -23
- package/src/components/islands/hooks.ts +36 -11
- package/src/components/layout/Analytics.astro +21 -79
- package/src/components/layout/Banner.astro +3 -1
- package/src/components/layout/DiscoveryLinks.astro +69 -0
- package/src/components/layout/Header.astro +93 -30
- package/src/components/layout/LanguageSwitcher.astro +4 -1
- package/src/components/layout/Logo.astro +23 -2
- package/src/components/layout/NavSelector.astro +13 -2
- package/src/components/layout/NavTree.astro +12 -3
- package/src/components/layout/NavTreeScript.astro +17 -3
- package/src/components/layout/PageActions.astro +3 -3
- package/src/components/layout/PageFeedback.astro +11 -1
- package/src/components/layout/PageLayout.astro +58 -11
- package/src/components/layout/Pagination.astro +7 -7
- package/src/components/layout/ReferenceLayout.astro +22 -13
- package/src/components/layout/RootLayout.astro +75 -72
- package/src/components/layout/Search.astro +43 -19
- package/src/components/layout/WebMcp.astro +1 -1
- package/src/components/layout/analytics-client.ts +73 -16
- package/src/components/layout/drawer-inert.ts +113 -15
- package/src/components/layout/dropdown-clamp.ts +105 -0
- package/src/components/layout/head-scripts.ts +15 -6
- package/src/components/layout/nav-utils.ts +17 -0
- package/src/components/layout/search/algolia.ts +8 -8
- package/src/components/layout/search/orama-cloud.ts +9 -7
- package/src/components/layout/search/typesense.ts +11 -17
- package/src/components/openapi/MessageComposer.astro +2 -2
- package/src/components/openapi/Playground.astro +2 -2
- package/src/components/openapi/description.ts +2 -2
- package/src/components/openapi/playground-client.ts +79 -10
- package/src/core/changelog-index.ts +24 -0
- package/src/core/code-fences.ts +1 -1
- package/src/core/component-overrides.ts +399 -154
- package/src/core/config-input.ts +201 -566
- package/src/core/config.ts +131 -42
- package/src/core/custom-pages.ts +105 -0
- package/src/core/data.ts +34 -15
- package/src/core/define-components.ts +12 -9
- package/src/core/deployment-env.ts +18 -74
- package/src/core/diagnostics.ts +14 -7
- package/src/core/graph.ts +69 -25
- package/src/core/i18n-ui.ts +41 -11
- package/src/core/i18n.ts +17 -0
- package/src/core/includes.ts +156 -38
- package/src/core/last-modified.ts +6 -11
- package/src/core/links.ts +166 -2
- package/src/core/manifest.ts +10 -3
- package/src/core/navigation.ts +115 -16
- package/src/core/new-tab.ts +35 -0
- package/src/core/node-require.ts +21 -0
- package/src/core/project-graph.ts +42 -26
- package/src/core/project.ts +9 -4
- package/src/core/request-body.ts +61 -0
- package/src/core/safe-href.ts +28 -0
- package/src/core/safe-links.ts +68 -0
- package/src/core/schema.ts +528 -745
- package/src/core/server-features.ts +10 -11
- package/src/core/sources/assets.ts +47 -11
- package/src/core/sources/collection.ts +67 -0
- package/src/core/sources/contentful-rich-text.ts +285 -0
- package/src/core/sources/contentful.ts +173 -0
- package/src/core/sources/github-releases.ts +51 -1
- package/src/core/sources/json.ts +71 -0
- package/src/core/sources/lexical.ts +195 -0
- package/src/core/sources/lower.ts +226 -0
- package/src/core/sources/normalize.ts +52 -2
- package/src/core/sources/notion.ts +39 -28
- package/src/core/sources/payload.ts +135 -0
- package/src/core/sources/portable-text.ts +11 -16
- package/src/core/sources/remote.ts +226 -0
- package/src/core/sources/resolve.ts +104 -166
- package/src/core/sources/sanity.ts +16 -49
- package/src/core/sources/strapi-blocks.ts +124 -0
- package/src/core/sources/strapi.ts +191 -0
- package/src/core/sources/types.ts +12 -1
- package/src/core/types.ts +15 -1
- package/src/core/ui-packs/ar.ts +10 -7
- package/src/core/ui-packs/bg.ts +10 -7
- package/src/core/ui-packs/bn.ts +10 -7
- package/src/core/ui-packs/ca.ts +7 -6
- package/src/core/ui-packs/cs.ts +10 -7
- package/src/core/ui-packs/da.ts +10 -7
- package/src/core/ui-packs/de.ts +10 -7
- package/src/core/ui-packs/el.ts +7 -6
- package/src/core/ui-packs/es.ts +7 -6
- package/src/core/ui-packs/fa.ts +10 -7
- package/src/core/ui-packs/fi.ts +10 -7
- package/src/core/ui-packs/fr.ts +7 -6
- package/src/core/ui-packs/he.ts +10 -7
- package/src/core/ui-packs/hi.ts +10 -7
- package/src/core/ui-packs/hr.ts +10 -7
- package/src/core/ui-packs/hu.ts +10 -7
- package/src/core/ui-packs/id.ts +10 -7
- package/src/core/ui-packs/it.ts +7 -6
- package/src/core/ui-packs/ja.ts +7 -6
- package/src/core/ui-packs/ko.ts +7 -6
- package/src/core/ui-packs/nl.ts +10 -7
- package/src/core/ui-packs/no.ts +10 -7
- package/src/core/ui-packs/pl.ts +10 -7
- package/src/core/ui-packs/pt-br.ts +7 -6
- package/src/core/ui-packs/pt.ts +7 -6
- package/src/core/ui-packs/ro.ts +10 -7
- package/src/core/ui-packs/ru.ts +10 -7
- package/src/core/ui-packs/sk.ts +10 -7
- package/src/core/ui-packs/sr.ts +10 -7
- package/src/core/ui-packs/sv.ts +10 -7
- package/src/core/ui-packs/th.ts +7 -6
- package/src/core/ui-packs/tr.ts +10 -7
- package/src/core/ui-packs/uk.ts +10 -7
- package/src/core/ui-packs/vi.ts +7 -6
- package/src/core/ui-packs/zh-tw.ts +7 -6
- package/src/core/ui-packs/zh.ts +7 -6
- package/src/core/unrecognized-keys.ts +10 -0
- package/src/core/version-cut.ts +116 -8
- package/src/deploy/adapter-output.ts +57 -97
- package/src/deploy/adapters/cloudflare.ts +39 -0
- package/src/deploy/adapters/index.ts +41 -0
- package/src/deploy/adapters/netlify.ts +34 -0
- package/src/deploy/adapters/node.ts +34 -0
- package/src/deploy/adapters/registry.ts +127 -0
- package/src/deploy/adapters/types.ts +92 -0
- package/src/deploy/adapters/vercel.ts +35 -0
- package/src/deploy/artifacts.ts +55 -27
- package/src/deploy/cloudflare-negotiation.ts +179 -100
- package/src/deploy/function-bundle.ts +18 -3
- package/src/deploy/headers.ts +124 -23
- package/src/deploy/node-headers.ts +198 -0
- package/src/deploy/platforms/cloudflare.ts +312 -0
- package/src/deploy/platforms/index.ts +67 -0
- package/src/deploy/platforms/netlify.ts +52 -0
- package/src/deploy/platforms/node.ts +40 -0
- package/src/deploy/platforms/paths.ts +42 -0
- package/src/deploy/platforms/static.ts +29 -0
- package/src/deploy/platforms/types.ts +112 -0
- package/src/deploy/platforms/vercel.ts +193 -0
- package/src/deploy/redirects.ts +30 -18
- package/src/deploy/robots.ts +3 -3
- package/src/deploy/rss.ts +2 -2
- package/src/deploy/sitemap.ts +2 -2
- package/src/deploy/vercel-negotiation.ts +2 -2
- package/src/eval/agents.ts +32 -1
- package/src/eval/findings.ts +19 -11
- package/src/eval/report.ts +10 -2
- package/src/markdown/external-links.ts +65 -0
- package/src/markdown/include.ts +45 -27
- package/src/markdown/index.ts +34 -9
- package/src/markdown/inline-code.ts +12 -6
- package/src/markdown/relative-links.ts +324 -0
- package/src/markdown/themes.ts +3 -3
- package/src/migrate/migrate.ts +149 -0
- package/src/openapi/parse.ts +64 -16
- package/src/openapi/proxy.ts +62 -10
- package/src/openapi/references.ts +147 -147
- package/src/openapi/render-mdx.ts +32 -10
- package/src/openapi/scalar.ts +15 -13
- package/src/openapi/sentence.ts +14 -0
- package/src/openapi/source.ts +37 -21
- package/src/openapi/spec-dependency-error.ts +15 -0
- package/src/reference/asyncapi.ts +83 -0
- package/src/reference/graphql.ts +89 -0
- package/src/reference/index.ts +40 -0
- package/src/reference/openapi.ts +83 -0
- package/src/reference/options.ts +201 -0
- package/src/reference/scalar.ts +136 -0
- package/src/reference/schema.ts +88 -0
- package/src/registry/eject.ts +109 -53
- package/src/search/adapters/algolia.ts +46 -0
- package/src/search/adapters/flexsearch.ts +29 -0
- package/src/search/adapters/index.ts +39 -0
- package/src/search/adapters/mixedbread.ts +39 -0
- package/src/search/adapters/orama-cloud.ts +51 -0
- package/src/search/adapters/orama.ts +24 -0
- package/src/search/adapters/pagefind.ts +27 -0
- package/src/search/adapters/registry.ts +131 -0
- package/src/search/adapters/types.ts +46 -0
- package/src/search/adapters/typesense.ts +57 -0
- package/src/search/build.ts +8 -4
- package/src/search/documents.ts +8 -2
- package/src/search/orama-index.ts +1 -1
- package/src/search/sync/algolia.ts +12 -11
- package/src/search/sync/index.ts +35 -20
- package/src/search/sync/orama-cloud.ts +12 -9
- package/src/search/sync/typesense.ts +15 -13
- package/src/sources/contentful.ts +63 -0
- package/src/sources/custom.ts +38 -0
- package/src/sources/filesystem.ts +51 -0
- package/src/sources/github-releases.ts +52 -0
- package/src/sources/index.ts +60 -0
- package/src/sources/mdx-remote.ts +76 -0
- package/src/sources/notion.ts +63 -0
- package/src/sources/obsidian.ts +38 -0
- package/src/sources/payload.ts +60 -0
- package/src/sources/registry.ts +182 -0
- package/src/sources/sanity.ts +66 -0
- package/src/sources/shared.ts +52 -0
- package/src/sources/strapi.ts +58 -0
- package/src/theme/entry.ts +24 -2
- package/src/translate/report.ts +40 -7
- package/src/upgrade/upgrade.ts +499 -0
- package/dist/cli/chunk-0qymqwzz.js +0 -164
- package/dist/cli/chunk-0qymqwzz.js.map +0 -15
- package/dist/cli/chunk-1jefwnfs.js.map +0 -48
- package/dist/cli/chunk-2mzebbbz.js +0 -69
- package/dist/cli/chunk-2z47ypj8.js.map +0 -11
- package/dist/cli/chunk-4x36ddpw.js.map +0 -11
- package/dist/cli/chunk-5093q3n7.js +0 -68
- package/dist/cli/chunk-5093q3n7.js.map +0 -10
- package/dist/cli/chunk-5qk08vmp.js.map +0 -11
- package/dist/cli/chunk-7s8hm3b6.js +0 -5347
- package/dist/cli/chunk-7s8hm3b6.js.map +0 -58
- package/dist/cli/chunk-8cjtbafj.js.map +0 -13
- package/dist/cli/chunk-97r59kpr.js +0 -381
- package/dist/cli/chunk-97r59kpr.js.map +0 -12
- package/dist/cli/chunk-ahnw3kxw.js.map +0 -15
- package/dist/cli/chunk-b27xqwn9.js.map +0 -10
- package/dist/cli/chunk-bf6bt1xt.js +0 -185
- package/dist/cli/chunk-bf6bt1xt.js.map +0 -11
- package/dist/cli/chunk-cjtn640a.js.map +0 -10
- package/dist/cli/chunk-ct47dqpx.js.map +0 -11
- package/dist/cli/chunk-esphfr8p.js +0 -107
- package/dist/cli/chunk-esphfr8p.js.map +0 -11
- package/dist/cli/chunk-ex56aa81.js +0 -1016
- package/dist/cli/chunk-ex56aa81.js.map +0 -13
- package/dist/cli/chunk-garjf5z9.js +0 -30
- package/dist/cli/chunk-garjf5z9.js.map +0 -10
- package/dist/cli/chunk-js7saxwm.js +0 -1045
- package/dist/cli/chunk-js7saxwm.js.map +0 -22
- package/dist/cli/chunk-k79xp7av.js.map +0 -39
- package/dist/cli/chunk-ps4m1xh4.js.map +0 -15
- package/dist/cli/chunk-q4rae3bg.js +0 -60
- package/dist/cli/chunk-q4rae3bg.js.map +0 -10
- package/dist/cli/chunk-rz9jmfhz.js +0 -108
- package/dist/cli/chunk-rz9jmfhz.js.map +0 -10
- package/dist/cli/chunk-vh9w1sgp.js +0 -73
- package/dist/cli/chunk-vh9w1sgp.js.map +0 -10
- package/dist/cli/chunk-vrfp10qk.js +0 -81
- package/dist/cli/chunk-vrfp10qk.js.map +0 -10
- package/dist/cli/chunk-yg63d42r.js.map +0 -34
- package/docs/advanced/api-reference.mdx +0 -240
- package/docs/configuration/ask-ai.mdx +0 -256
- package/docs/reference/cli.mdx +0 -197
- package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +0 -20
- package/src/components/content/changelog-element.ts +0 -69
- package/src/search/providers.ts +0 -91
- /package/dist/cli/{chunk-vacwm2hv.js.map → chunk-27g6wdth.js.map} +0 -0
- /package/dist/cli/{chunk-dwgcp5sm.js.map → chunk-ce574jw2.js.map} +0 -0
- /package/dist/cli/{chunk-e7f42gdj.js.map → chunk-fxypxtvm.js.map} +0 -0
- /package/dist/cli/{chunk-nn13znc2.js.map → chunk-zxcczpyx.js.map} +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { createSatteriMarkdownProcessor } from "@astrojs/markdown-satteri";
|
|
3
|
+
import data from "blume:data";
|
|
3
4
|
|
|
5
|
+
import { contentStrings } from "./content-strings.ts";
|
|
4
6
|
import { escapeRawHtml, unwrapParagraph } from "./inline-markdown.ts";
|
|
7
|
+
import { promptMarkdown } from "./prompt-markdown.ts";
|
|
5
8
|
|
|
6
9
|
interface Props {
|
|
7
10
|
actions?: boolean | string | string[];
|
|
@@ -26,6 +29,15 @@ const renderedDescription = await markdownProcessor.render(
|
|
|
26
29
|
escapeRawHtml(description)
|
|
27
30
|
);
|
|
28
31
|
const descriptionHtml = unwrapParagraph(renderedDescription.code);
|
|
32
|
+
// The prompt as Markdown, for the copy button and the Cursor link: the rendered
|
|
33
|
+
// body's text alone would drop its links, list markers, and code fences.
|
|
34
|
+
const promptSource = Astro.slots.has("default")
|
|
35
|
+
? promptMarkdown(await Astro.slots.render("default"))
|
|
36
|
+
: "";
|
|
37
|
+
const { actions: actionStrings, content: strings } = contentStrings(
|
|
38
|
+
data,
|
|
39
|
+
Astro.currentLocale
|
|
40
|
+
);
|
|
29
41
|
|
|
30
42
|
const primaryButton =
|
|
31
43
|
"inline-flex h-8 cursor-pointer items-center rounded-lg bg-accent px-3.5 font-medium text-accent-foreground text-xs transition hover:bg-accent/90";
|
|
@@ -35,6 +47,7 @@ const secondaryButton =
|
|
|
35
47
|
|
|
36
48
|
<blume-prompt
|
|
37
49
|
class="not-prose my-6 block rounded-blume border border-border bg-muted/30"
|
|
50
|
+
data-blume-prompt-markdown={promptSource}
|
|
38
51
|
>
|
|
39
52
|
<div class="flex items-center justify-between gap-3 px-4 py-3">
|
|
40
53
|
<p
|
|
@@ -47,39 +60,35 @@ const secondaryButton =
|
|
|
47
60
|
<div class="flex shrink-0 items-center gap-2">
|
|
48
61
|
{enabledActions.has("copy") && (
|
|
49
62
|
<button
|
|
50
|
-
aria-label="Copy prompt"
|
|
51
63
|
class={primaryButton}
|
|
52
64
|
data-blume-prompt-copy
|
|
65
|
+
data-copied={actionStrings.copied}
|
|
53
66
|
type="button"
|
|
54
67
|
>
|
|
55
68
|
{/* Both labels share one grid cell so the button is always sized
|
|
56
|
-
to the wider
|
|
57
|
-
|
|
69
|
+
to the wider one — swapping to the copied label on copy can't
|
|
70
|
+
resize it and shift the description beside it. */}
|
|
58
71
|
<span class="grid *:col-start-1 *:row-start-1 *:text-center">
|
|
59
|
-
<span data-blume-prompt-copy-idle>
|
|
72
|
+
<span data-blume-prompt-copy-idle>{strings.copyPrompt}</span>
|
|
60
73
|
<span aria-hidden="true" class="invisible" data-blume-prompt-copy-done>
|
|
61
|
-
|
|
74
|
+
{actionStrings.copied}
|
|
62
75
|
</span>
|
|
63
76
|
</span>
|
|
64
77
|
</button>
|
|
65
78
|
)}
|
|
66
79
|
{enabledActions.has("cursor") && (
|
|
67
80
|
<a
|
|
68
|
-
aria-label="Open prompt in Cursor"
|
|
69
81
|
class={secondaryButton}
|
|
70
82
|
data-blume-prompt-cursor
|
|
71
|
-
href=
|
|
83
|
+
href={`cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(promptSource)}`}
|
|
72
84
|
>
|
|
73
|
-
|
|
85
|
+
{actionStrings.openIn.replace("{name}", "Cursor")}
|
|
74
86
|
</a>
|
|
75
87
|
)}
|
|
76
88
|
</div>
|
|
77
89
|
)
|
|
78
90
|
}
|
|
79
91
|
</div>
|
|
80
|
-
<div data-blume-prompt-content hidden>
|
|
81
|
-
<slot />
|
|
82
|
-
</div>
|
|
83
92
|
</blume-prompt>
|
|
84
93
|
|
|
85
94
|
<script>
|
|
@@ -87,17 +96,9 @@ const secondaryButton =
|
|
|
87
96
|
|
|
88
97
|
class BlumePrompt extends HTMLElement {
|
|
89
98
|
connectedCallback() {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const promptText = () => content?.textContent?.trim() ?? "";
|
|
94
|
-
|
|
95
|
-
const cursor = this.querySelector<HTMLAnchorElement>(
|
|
96
|
-
"[data-blume-prompt-cursor]"
|
|
97
|
-
);
|
|
98
|
-
if (cursor) {
|
|
99
|
-
cursor.href = `cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(promptText())}`;
|
|
100
|
-
}
|
|
99
|
+
// Serialized to Markdown at build time; the Cursor link already carries
|
|
100
|
+
// it in its href.
|
|
101
|
+
const promptText = this.dataset.blumePromptMarkdown ?? "";
|
|
101
102
|
|
|
102
103
|
const copy = this.querySelector<HTMLButtonElement>(
|
|
103
104
|
"[data-blume-prompt-copy]"
|
|
@@ -113,10 +114,9 @@ const secondaryButton =
|
|
|
113
114
|
const flash = createCopyFlash((copied) => {
|
|
114
115
|
idle?.classList.toggle("invisible", copied);
|
|
115
116
|
done?.classList.toggle("invisible", !copied);
|
|
116
|
-
}, "Copied");
|
|
117
|
+
}, copy?.dataset.copied ?? "Copied!");
|
|
117
118
|
copy?.addEventListener("click", async () => {
|
|
118
|
-
|
|
119
|
-
if (text && (await copyText(text))) {
|
|
119
|
+
if (promptText && (await copyText(promptText))) {
|
|
120
120
|
flash();
|
|
121
121
|
}
|
|
122
122
|
});
|
|
@@ -272,7 +272,9 @@ const useDropdown = dropdown && !inline;
|
|
|
272
272
|
url.searchParams.set(param, id);
|
|
273
273
|
history.replaceState(history.state, "", url);
|
|
274
274
|
} else if (this.#hashEnabled()) {
|
|
275
|
-
|
|
275
|
+
// Keep the entry's state here too: the client router ignores a
|
|
276
|
+
// popstate whose state is null, so Back would change only the URL.
|
|
277
|
+
history.replaceState(history.state, "", `#${encodeURIComponent(id)}`);
|
|
276
278
|
}
|
|
277
279
|
}
|
|
278
280
|
if (sync && this.#syncEnabled()) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { newTabAttrs } from "../../core/new-tab.ts";
|
|
2
3
|
import Icon from "../Icon.astro";
|
|
3
4
|
import { contentHref } from "./base-href.ts";
|
|
4
5
|
|
|
5
6
|
const { description, href, title } = Astro.props;
|
|
6
7
|
const Tag = href ? "a" : "div";
|
|
7
|
-
const external = href?.startsWith("http");
|
|
8
8
|
const previewPattern =
|
|
9
9
|
"background-image:radial-gradient(circle at 1px 1px,color-mix(in oklab,currentColor 18%,transparent) 1px,transparent 0);background-size:18px 18px;";
|
|
10
10
|
---
|
|
@@ -13,8 +13,7 @@ const previewPattern =
|
|
|
13
13
|
class="not-prose block overflow-hidden rounded-blume border border-border bg-background text-inherit no-underline! transition-colors hover:no-underline! hover:[&[href]]:border-accent"
|
|
14
14
|
data-blume-tile
|
|
15
15
|
href={href === undefined ? undefined : contentHref(href)}
|
|
16
|
-
|
|
17
|
-
target={external ? "_blank" : undefined}
|
|
16
|
+
{...newTabAttrs(href)}
|
|
18
17
|
>
|
|
19
18
|
<div
|
|
20
19
|
class="flex aspect-[16/9] min-h-32 items-center justify-center bg-muted/40 p-4 text-muted-foreground [&_img]:max-h-full [&_img]:max-w-full [&_svg]:max-h-full [&_svg]:max-w-full"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { newTabAttrs } from "../../core/new-tab.ts";
|
|
4
3
|
import { contentHref } from "./base-href.ts";
|
|
5
4
|
import { unwrapParagraph } from "./inline-markdown.ts";
|
|
5
|
+
import { tooltipId as tooltipIdFor } from "./tooltip-id.ts";
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
8
8
|
cta?: string;
|
|
@@ -12,15 +12,24 @@ interface Props {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const { cta, headline, href, tip } = Astro.props;
|
|
15
|
-
const tooltipId = `blume-tooltip-${randomUUID()}`;
|
|
16
|
-
const external = href?.startsWith("http");
|
|
17
15
|
|
|
18
16
|
const labelHtml = Astro.slots.has("default")
|
|
19
17
|
? unwrapParagraph(await Astro.slots.render("default"))
|
|
20
18
|
: "";
|
|
19
|
+
// Derived from the content, so an unchanged page rebuilds byte-identical.
|
|
20
|
+
const tooltipId = tooltipIdFor(Astro.locals, [
|
|
21
|
+
labelHtml,
|
|
22
|
+
headline,
|
|
23
|
+
tip,
|
|
24
|
+
cta,
|
|
25
|
+
href,
|
|
26
|
+
]);
|
|
21
27
|
---
|
|
22
28
|
|
|
23
|
-
<span
|
|
29
|
+
<span
|
|
30
|
+
class="not-prose relative inline-flex cursor-help items-baseline"
|
|
31
|
+
data-blume-tooltip
|
|
32
|
+
>
|
|
24
33
|
<span
|
|
25
34
|
aria-describedby={tooltipId}
|
|
26
35
|
class="underline decoration-dotted underline-offset-4"
|
|
@@ -41,8 +50,7 @@ const labelHtml = Astro.slots.has("default")
|
|
|
41
50
|
<a
|
|
42
51
|
class="mt-2 inline-flex font-medium text-accent text-xs hover:underline"
|
|
43
52
|
href={contentHref(href)}
|
|
44
|
-
|
|
45
|
-
target={external ? "_blank" : undefined}
|
|
53
|
+
{...newTabAttrs(href)}
|
|
46
54
|
>
|
|
47
55
|
{cta}
|
|
48
56
|
</a>
|
|
@@ -57,9 +65,49 @@ const labelHtml = Astro.slots.has("default")
|
|
|
57
65
|
visibility: hidden;
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
/* Focus anywhere inside keeps it open, so a keyboard reader can Tab from
|
|
69
|
+
the trigger to the call-to-action link without the panel vanishing. */
|
|
70
|
+
[data-blume-tooltip]:hover [data-blume-tooltip-panel],
|
|
71
|
+
[data-blume-tooltip]:focus-within [data-blume-tooltip-panel] {
|
|
62
72
|
pointer-events: auto;
|
|
63
73
|
visibility: visible;
|
|
64
74
|
}
|
|
75
|
+
|
|
76
|
+
/* Escape hides it until the pointer or focus leaves (WCAG 1.4.13). */
|
|
77
|
+
[data-blume-tooltip][data-dismissed] [data-blume-tooltip-panel] {
|
|
78
|
+
pointer-events: none;
|
|
79
|
+
visibility: hidden;
|
|
80
|
+
}
|
|
65
81
|
</style>
|
|
82
|
+
|
|
83
|
+
<script>
|
|
84
|
+
// Delegated from the document, so tooltips a client-router swap brings in
|
|
85
|
+
// are covered without re-binding; the module runs once per real load.
|
|
86
|
+
const TOOLTIP = "[data-blume-tooltip]";
|
|
87
|
+
|
|
88
|
+
document.addEventListener("keydown", (event) => {
|
|
89
|
+
if (event.key !== "Escape") {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
for (const tooltip of document.querySelectorAll(TOOLTIP)) {
|
|
93
|
+
if (tooltip.matches(":hover, :focus-within")) {
|
|
94
|
+
tooltip.setAttribute("data-dismissed", "");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Leaving the tooltip re-arms it for the next hover or focus.
|
|
100
|
+
const rearm = (event: FocusEvent | PointerEvent) => {
|
|
101
|
+
const tooltip =
|
|
102
|
+
event.target instanceof Element ? event.target.closest(TOOLTIP) : null;
|
|
103
|
+
const { relatedTarget } = event;
|
|
104
|
+
if (
|
|
105
|
+
tooltip &&
|
|
106
|
+
!(relatedTarget instanceof Node && tooltip.contains(relatedTarget))
|
|
107
|
+
) {
|
|
108
|
+
tooltip.removeAttribute("data-dismissed");
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
document.addEventListener("focusout", rearm);
|
|
112
|
+
document.addEventListener("pointerout", rearm);
|
|
113
|
+
</script>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { EN_UI } from "../../core/i18n-ui.ts";
|
|
2
|
+
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
3
|
+
|
|
4
|
+
/** The `blume:data` fields a content component resolves its labels from. */
|
|
5
|
+
export interface ContentStringsData {
|
|
6
|
+
config: { i18n: object | null };
|
|
7
|
+
ui: UIStrings;
|
|
8
|
+
uiByLocale: Record<string, UIStrings>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** The UI string groups a content component's chrome reads. */
|
|
12
|
+
export type ContentStrings = Pick<UIStrings, "actions" | "content">;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Labels for a component rendered inside page content (MDX), which gets no
|
|
16
|
+
* strings prop from the layout the way chrome components do: the dictionary of
|
|
17
|
+
* the locale Astro resolved for the URL (`Astro.currentLocale`, the page's own
|
|
18
|
+
* locale — a fallback page included, matching its chrome), else the site
|
|
19
|
+
* default. Merged over the English baseline per group, so a key a translation
|
|
20
|
+
* lacks still renders instead of coming out blank.
|
|
21
|
+
*/
|
|
22
|
+
export const contentStrings = (
|
|
23
|
+
data: ContentStringsData,
|
|
24
|
+
currentLocale?: string
|
|
25
|
+
): ContentStrings => {
|
|
26
|
+
const dictionary =
|
|
27
|
+
data.config.i18n && currentLocale
|
|
28
|
+
? (data.uiByLocale[currentLocale] ?? data.ui)
|
|
29
|
+
: data.ui;
|
|
30
|
+
return {
|
|
31
|
+
actions: { ...EN_UI.actions, ...dictionary.actions },
|
|
32
|
+
content: { ...EN_UI.content, ...dictionary.content },
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -36,7 +36,7 @@ export interface DiffOptions {
|
|
|
36
36
|
/** Path to a `.patch`/`.diff` file, resolved relative to {@link DiffOptions.root}. */
|
|
37
37
|
src?: string;
|
|
38
38
|
/**
|
|
39
|
-
* Light/dark Shiki themes (`markdown.
|
|
39
|
+
* Light/dark Shiki themes (`markdown.code.theme`). Defaults to the same
|
|
40
40
|
* github pair Blume's code blocks use, keeping diffs in lockstep.
|
|
41
41
|
*/
|
|
42
42
|
theme?: CodeThemes;
|
|
@@ -23,7 +23,10 @@ const prefersDark = () => document.documentElement.dataset.theme === "dark";
|
|
|
23
23
|
|
|
24
24
|
let counter = 0;
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
// Exported so this file is a module, not a script: `blume:features` lazy-loads
|
|
27
|
+
// it with `import()`, and `astro check` rejects a dynamic import of a file with
|
|
28
|
+
// no import or export of its own (ts(2306)) on every site with a diagram.
|
|
29
|
+
export class BlumeMermaid extends HTMLElement {
|
|
27
30
|
#observer: MutationObserver | null = null;
|
|
28
31
|
#renderToken = 0;
|
|
29
32
|
|
|
@@ -84,8 +87,16 @@ class BlumeMermaid extends HTMLElement {
|
|
|
84
87
|
// Re-render on color-theme changes so the diagram tracks light and dark.
|
|
85
88
|
// One observer per connection, disconnected on removal — otherwise every
|
|
86
89
|
// DOM move stacks another observer that renders into detached DOM forever.
|
|
90
|
+
// The theme script rewrites `data-theme` after every client-router swap,
|
|
91
|
+
// usually to the value it already had, so only a real flip re-renders.
|
|
92
|
+
let dark = prefersDark();
|
|
87
93
|
this.#observer?.disconnect();
|
|
88
|
-
this.#observer = new MutationObserver(() =>
|
|
94
|
+
this.#observer = new MutationObserver(() => {
|
|
95
|
+
if (prefersDark() !== dark) {
|
|
96
|
+
dark = prefersDark();
|
|
97
|
+
render();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
89
100
|
this.#observer.observe(document.documentElement, {
|
|
90
101
|
attributeFilter: ["data-theme"],
|
|
91
102
|
});
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { NodeType, parse } from "node-html-parser";
|
|
2
|
+
import type { HTMLElement, Node } from "node-html-parser";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A `<Prompt>`'s body as Markdown, for its copy button and "Open in Cursor"
|
|
6
|
+
* link. The component only ever sees its slot as rendered HTML, so this turns
|
|
7
|
+
* that HTML back into the Markdown a reader pastes into an AI tool — keeping
|
|
8
|
+
* link URLs, list markers, emphasis, and code fences that the element's plain
|
|
9
|
+
* `textContent` would drop.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Elements that carry no prompt text: chrome, media, and hidden content. */
|
|
13
|
+
const SKIPPED = new Set(["BUTTON", "SCRIPT", "STYLE", "SVG", "TEMPLATE"]);
|
|
14
|
+
|
|
15
|
+
const HEADING = /^H(?<level>[1-6])$/u;
|
|
16
|
+
|
|
17
|
+
/** Elements that start a block of their own rather than flowing inline. */
|
|
18
|
+
const BLOCKS = new Set([
|
|
19
|
+
"BLOCKQUOTE",
|
|
20
|
+
"DETAILS",
|
|
21
|
+
"DIV",
|
|
22
|
+
"FIGURE",
|
|
23
|
+
"H1",
|
|
24
|
+
"H2",
|
|
25
|
+
"H3",
|
|
26
|
+
"H4",
|
|
27
|
+
"H5",
|
|
28
|
+
"H6",
|
|
29
|
+
"HR",
|
|
30
|
+
"OL",
|
|
31
|
+
"P",
|
|
32
|
+
"PRE",
|
|
33
|
+
"SECTION",
|
|
34
|
+
"TABLE",
|
|
35
|
+
"UL",
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const isElement = (node: Node): node is HTMLElement =>
|
|
39
|
+
node.nodeType === NodeType.ELEMENT_NODE;
|
|
40
|
+
|
|
41
|
+
const isSkipped = (element: HTMLElement): boolean =>
|
|
42
|
+
SKIPPED.has(element.tagName) ||
|
|
43
|
+
element.hasAttribute("hidden") ||
|
|
44
|
+
element.getAttribute("aria-hidden") === "true";
|
|
45
|
+
|
|
46
|
+
/** A backtick run one longer than any inside `text`, so it can't close early. */
|
|
47
|
+
const fenceFor = (text: string, minimum: number): string => {
|
|
48
|
+
const longest = Math.max(
|
|
49
|
+
0,
|
|
50
|
+
...(text.match(/`+/gu) ?? []).map((run) => run.length)
|
|
51
|
+
);
|
|
52
|
+
return "`".repeat(Math.max(minimum, longest + 1));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Wrap `inner` in `marker`, keeping edge spaces outside the markers. */
|
|
56
|
+
const wrap = (marker: string, inner: string): string => {
|
|
57
|
+
const trimmed = inner.trim();
|
|
58
|
+
if (!trimmed) {
|
|
59
|
+
return inner;
|
|
60
|
+
}
|
|
61
|
+
const lead = inner.startsWith(" ") ? " " : "";
|
|
62
|
+
const trail = inner.endsWith(" ") ? " " : "";
|
|
63
|
+
return `${lead}${marker}${trimmed}${marker}${trail}`;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const inlineCode = (text: string): string => {
|
|
67
|
+
const fence = fenceFor(text, 1);
|
|
68
|
+
const pad = text.startsWith("`") || text.endsWith("`") ? " " : "";
|
|
69
|
+
return `${fence}${pad}${text}${pad}${fence}`;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Finish an inline run: HTML collapses whitespace across element edges too, so
|
|
74
|
+
* the space a text node and an emphasis marker each kept becomes one.
|
|
75
|
+
*/
|
|
76
|
+
const collapse = (text: string): string =>
|
|
77
|
+
text.replaceAll(/ {2,}/gu, " ").trim();
|
|
78
|
+
|
|
79
|
+
const inline = (node: Node): string => {
|
|
80
|
+
if (!isElement(node)) {
|
|
81
|
+
// HTML collapses runs of whitespace (source newlines included) to one space.
|
|
82
|
+
return node.text.replaceAll(/\s+/gu, " ");
|
|
83
|
+
}
|
|
84
|
+
if (isSkipped(node)) {
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
const children = () => node.childNodes.map(inline).join("");
|
|
88
|
+
switch (node.tagName) {
|
|
89
|
+
case "A": {
|
|
90
|
+
const href = node.getAttribute("href");
|
|
91
|
+
const label = children().trim();
|
|
92
|
+
if (!href) {
|
|
93
|
+
return label;
|
|
94
|
+
}
|
|
95
|
+
return label && label !== href ? `[${label}](${href})` : href;
|
|
96
|
+
}
|
|
97
|
+
case "B":
|
|
98
|
+
case "STRONG": {
|
|
99
|
+
return wrap("**", children());
|
|
100
|
+
}
|
|
101
|
+
case "BR": {
|
|
102
|
+
return "\n";
|
|
103
|
+
}
|
|
104
|
+
case "CODE": {
|
|
105
|
+
return inlineCode(node.text);
|
|
106
|
+
}
|
|
107
|
+
case "DEL":
|
|
108
|
+
case "S": {
|
|
109
|
+
return wrap("~~", children());
|
|
110
|
+
}
|
|
111
|
+
case "EM":
|
|
112
|
+
case "I": {
|
|
113
|
+
return wrap("_", children());
|
|
114
|
+
}
|
|
115
|
+
case "IMG": {
|
|
116
|
+
const src = node.getAttribute("src");
|
|
117
|
+
return src ? `` : "";
|
|
118
|
+
}
|
|
119
|
+
default: {
|
|
120
|
+
return children();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/** A line-by-line indent, leaving blank lines blank. */
|
|
126
|
+
const indent = (text: string, prefix: string): string =>
|
|
127
|
+
text
|
|
128
|
+
.split("\n")
|
|
129
|
+
.map((line) => (line ? `${prefix}${line}` : line))
|
|
130
|
+
.join("\n");
|
|
131
|
+
|
|
132
|
+
const codeBlock = (pre: HTMLElement): string => {
|
|
133
|
+
const code = pre.querySelector("code");
|
|
134
|
+
const language =
|
|
135
|
+
pre.attributes["data-language"] ??
|
|
136
|
+
/\blanguage-(?<lang>\S+)/u.exec(code?.getAttribute("class") ?? "")?.groups
|
|
137
|
+
?.lang ??
|
|
138
|
+
"";
|
|
139
|
+
const text = (code ?? pre).text.replace(/\n$/u, "");
|
|
140
|
+
const fence = fenceFor(text, 3);
|
|
141
|
+
return `${fence}${language}\n${text}\n${fence}`;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const listBlock = (list: HTMLElement): string => {
|
|
145
|
+
const ordered = list.tagName === "OL";
|
|
146
|
+
let number = Number(list.getAttribute("start") ?? "1") || 1;
|
|
147
|
+
const items: string[] = [];
|
|
148
|
+
for (const item of list.childNodes) {
|
|
149
|
+
if (!isElement(item) || item.tagName !== "LI") {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const marker = ordered ? `${number}.` : "-";
|
|
153
|
+
number += 1;
|
|
154
|
+
// A tight item (no paragraphs of its own) keeps its nested list on the
|
|
155
|
+
// next line; a loose one separates its blocks with a blank line.
|
|
156
|
+
const loose = item.childNodes.some(
|
|
157
|
+
(child) => isElement(child) && child.tagName === "P"
|
|
158
|
+
);
|
|
159
|
+
// oxlint-disable-next-line no-use-before-define -- mutual recursion: an item holds blocks, including nested lists
|
|
160
|
+
const body = blocks(item).join(loose ? "\n\n" : "\n");
|
|
161
|
+
const [first = "", ...rest] = body.split("\n");
|
|
162
|
+
const continuation = indent(rest.join("\n"), " ".repeat(marker.length + 1));
|
|
163
|
+
items.push(
|
|
164
|
+
continuation
|
|
165
|
+
? `${marker} ${first}\n${continuation}`
|
|
166
|
+
: `${marker} ${first}`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
return items.join("\n");
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/** A GFM delimiter cell per column alignment; unaligned columns get `---`. */
|
|
173
|
+
const DELIMITERS = new Map([
|
|
174
|
+
["center", ":---:"],
|
|
175
|
+
["left", ":---"],
|
|
176
|
+
["right", "---:"],
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
const TEXT_ALIGN = /text-align:\s*(?<align>center|left|right)/u;
|
|
180
|
+
|
|
181
|
+
/** A cell's alignment, from the `align` attribute or an inline style. */
|
|
182
|
+
const cellAlign = (cell: HTMLElement | undefined): string =>
|
|
183
|
+
cell?.getAttribute("align") ??
|
|
184
|
+
TEXT_ALIGN.exec(cell?.getAttribute("style") ?? "")?.groups?.align ??
|
|
185
|
+
"";
|
|
186
|
+
|
|
187
|
+
/** A cell's inline Markdown on one line, its pipes escaped. */
|
|
188
|
+
const tableCell = (cell: HTMLElement | undefined): string =>
|
|
189
|
+
cell
|
|
190
|
+
? collapse(inline(cell).replaceAll("\n", " ")).replaceAll(
|
|
191
|
+
"|",
|
|
192
|
+
String.raw`\|`
|
|
193
|
+
)
|
|
194
|
+
: "";
|
|
195
|
+
|
|
196
|
+
const isCell = (node: Node): node is HTMLElement =>
|
|
197
|
+
isElement(node) && (node.tagName === "TD" || node.tagName === "TH");
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* A table as a GFM table: its first row as the header (GFM requires one), a
|
|
201
|
+
* delimiter row carrying each column's alignment, then the body rows, padded
|
|
202
|
+
* to the widest row.
|
|
203
|
+
*/
|
|
204
|
+
const tableBlock = (table: HTMLElement): string => {
|
|
205
|
+
const rows = table
|
|
206
|
+
.querySelectorAll("tr")
|
|
207
|
+
.filter((row) => !isSkipped(row))
|
|
208
|
+
.map((row) => row.childNodes.filter(isCell));
|
|
209
|
+
const [head, ...body] = rows;
|
|
210
|
+
if (!head) {
|
|
211
|
+
return "";
|
|
212
|
+
}
|
|
213
|
+
const width = Math.max(...rows.map((row) => row.length));
|
|
214
|
+
const columns = Array.from({ length: width }, (_, index) => index);
|
|
215
|
+
const row = (cells: HTMLElement[]): string =>
|
|
216
|
+
`| ${columns.map((index) => tableCell(cells[index])).join(" | ")} |`;
|
|
217
|
+
const delimiter = `| ${columns
|
|
218
|
+
.map((index) => DELIMITERS.get(cellAlign(head[index])) ?? "---")
|
|
219
|
+
.join(" | ")} |`;
|
|
220
|
+
return [row(head), delimiter, ...body.map(row)].join("\n");
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const block = (element: HTMLElement): string[] => {
|
|
224
|
+
const heading = HEADING.exec(element.tagName)?.groups?.level;
|
|
225
|
+
if (heading) {
|
|
226
|
+
return [`${"#".repeat(Number(heading))} ${collapse(inline(element))}`];
|
|
227
|
+
}
|
|
228
|
+
switch (element.tagName) {
|
|
229
|
+
case "BLOCKQUOTE": {
|
|
230
|
+
// Every line is quoted, blank ones included, so the paragraphs stay in
|
|
231
|
+
// one quote.
|
|
232
|
+
// oxlint-disable-next-line no-use-before-define -- mutual recursion: a quote holds blocks
|
|
233
|
+
const quoted = blocks(element)
|
|
234
|
+
.join("\n\n")
|
|
235
|
+
.split("\n")
|
|
236
|
+
.map((line) => (line ? `> ${line}` : ">"));
|
|
237
|
+
return [quoted.join("\n")];
|
|
238
|
+
}
|
|
239
|
+
case "HR": {
|
|
240
|
+
return ["---"];
|
|
241
|
+
}
|
|
242
|
+
case "OL":
|
|
243
|
+
case "UL": {
|
|
244
|
+
return [listBlock(element)];
|
|
245
|
+
}
|
|
246
|
+
case "P": {
|
|
247
|
+
return [collapse(inline(element))];
|
|
248
|
+
}
|
|
249
|
+
case "PRE": {
|
|
250
|
+
return [codeBlock(element)];
|
|
251
|
+
}
|
|
252
|
+
case "TABLE": {
|
|
253
|
+
return [tableBlock(element)];
|
|
254
|
+
}
|
|
255
|
+
default: {
|
|
256
|
+
// oxlint-disable-next-line no-use-before-define -- mutual recursion: a container holds blocks
|
|
257
|
+
return blocks(element);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/** A container's children as Markdown blocks, inline runs grouped into one. */
|
|
263
|
+
const blocks = (container: HTMLElement): string[] => {
|
|
264
|
+
const out: string[] = [];
|
|
265
|
+
let run = "";
|
|
266
|
+
const flush = () => {
|
|
267
|
+
const text = collapse(run);
|
|
268
|
+
if (text) {
|
|
269
|
+
out.push(text);
|
|
270
|
+
}
|
|
271
|
+
run = "";
|
|
272
|
+
};
|
|
273
|
+
for (const child of container.childNodes) {
|
|
274
|
+
if (isElement(child) && BLOCKS.has(child.tagName)) {
|
|
275
|
+
flush();
|
|
276
|
+
if (!isSkipped(child)) {
|
|
277
|
+
out.push(...block(child).filter(Boolean));
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
run += inline(child);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
flush();
|
|
284
|
+
return out;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export const promptMarkdown = (html: string): string =>
|
|
288
|
+
// `pre` is parsed as elements (not raw text) so a code block's `<code>` and
|
|
289
|
+
// its language attribute are reachable.
|
|
290
|
+
blocks(parse(html, { blockTextElements: { script: true, style: true } }))
|
|
291
|
+
.join("\n\n")
|
|
292
|
+
.trim();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-render bookkeeping for stable element ids, kept on the request's
|
|
5
|
+
* `Astro.locals` so every component in one page render — layout and MDX
|
|
6
|
+
* content alike — shares it, and concurrent page renders never see each
|
|
7
|
+
* other's.
|
|
8
|
+
*/
|
|
9
|
+
export interface StableIdLocals {
|
|
10
|
+
/** Prefix + content digest → how many elements on this page carried it. */
|
|
11
|
+
blumeStableIds?: Map<string, number>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* An element id derived from what the element renders, not drawn at random,
|
|
16
|
+
* so rebuilding an unchanged page produces the same HTML. A second identical
|
|
17
|
+
* element on the same page gets a numbered suffix to keep ids unique;
|
|
18
|
+
* identical elements render identical markup, so which copy is numbered never
|
|
19
|
+
* changes the page's bytes.
|
|
20
|
+
*/
|
|
21
|
+
export const stableId = (
|
|
22
|
+
locals: StableIdLocals,
|
|
23
|
+
prefix: string,
|
|
24
|
+
content: readonly (string | undefined)[]
|
|
25
|
+
): string => {
|
|
26
|
+
const digest = createHash("sha256")
|
|
27
|
+
.update(JSON.stringify(content))
|
|
28
|
+
.digest("hex")
|
|
29
|
+
.slice(0, 12);
|
|
30
|
+
const base = `${prefix}-${digest}`;
|
|
31
|
+
locals.blumeStableIds ??= new Map();
|
|
32
|
+
const count = (locals.blumeStableIds.get(base) ?? 0) + 1;
|
|
33
|
+
locals.blumeStableIds.set(base, count);
|
|
34
|
+
return count === 1 ? base : `${base}-${count}`;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** The element id a tooltip's panel and trigger pair on. */
|
|
38
|
+
export const tooltipId = (
|
|
39
|
+
locals: StableIdLocals,
|
|
40
|
+
content: readonly (string | undefined)[]
|
|
41
|
+
): string => stableId(locals, "blume-tooltip", content);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared clipboard + "Copied" feedback used by every copy affordance (code
|
|
3
|
-
* blocks, page actions, color swatches, prompts, API panels,
|
|
3
|
+
* blocks, page actions, color swatches, prompts, API panels, the assistant). One
|
|
4
4
|
* implementation owns the invariants each site used to hand-roll:
|
|
5
5
|
*
|
|
6
6
|
* - the clipboard write is guarded and a confirmation must never lie: a
|