blume 2.1.3 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +134 -0
- package/README.md +4 -4
- package/dist/cli/{chunk-y4msrwm9.js → chunk-080mfch6.js} +15 -15
- package/dist/cli/{chunk-gfc5fqvf.js → chunk-1ch3ct3r.js} +18 -18
- package/dist/cli/{chunk-gfc5fqvf.js.map → chunk-1ch3ct3r.js.map} +1 -1
- package/dist/cli/{chunk-32khwcb6.js → chunk-2ssh60qh.js} +42 -69
- package/dist/cli/{chunk-32khwcb6.js.map → chunk-2ssh60qh.js.map} +9 -10
- package/dist/cli/{chunk-9qzgg6mj.js → chunk-34wap3ed.js} +3 -3
- package/dist/cli/{chunk-k3gr1nm9.js → chunk-40vk8kgf.js} +13 -16
- package/dist/cli/{chunk-k3gr1nm9.js.map → chunk-40vk8kgf.js.map} +3 -3
- package/dist/cli/chunk-487b95qy.js +329 -0
- package/dist/cli/chunk-487b95qy.js.map +13 -0
- package/dist/cli/chunk-4zxh25hc.js +157 -0
- package/dist/cli/chunk-4zxh25hc.js.map +10 -0
- package/dist/cli/chunk-69rjf786.js +146 -0
- package/dist/cli/chunk-69rjf786.js.map +11 -0
- package/dist/cli/{chunk-dqhg54mr.js → chunk-9h8mh2tk.js} +3 -3
- package/dist/cli/{chunk-dqhg54mr.js.map → chunk-9h8mh2tk.js.map} +2 -2
- package/dist/cli/{chunk-bqxaqn5p.js → chunk-9jqmq5hv.js} +8 -8
- package/dist/cli/{chunk-bqxaqn5p.js.map → chunk-9jqmq5hv.js.map} +1 -1
- package/dist/cli/{chunk-dv2z0rzf.js → chunk-aafhwx74.js} +38 -13
- package/dist/cli/{chunk-dv2z0rzf.js.map → chunk-aafhwx74.js.map} +5 -4
- package/dist/cli/{chunk-desvm72z.js → chunk-b680sv30.js} +22 -26
- package/dist/cli/chunk-b680sv30.js.map +13 -0
- package/dist/cli/{chunk-0w6hs479.js → chunk-beb0z983.js} +30 -18
- package/dist/cli/chunk-beb0z983.js.map +10 -0
- package/dist/cli/{chunk-1f882900.js → chunk-c5bjxq8v.js} +2 -2
- package/dist/cli/chunk-e1n41w9m.js +155 -0
- package/dist/cli/chunk-e1n41w9m.js.map +11 -0
- package/dist/cli/{chunk-sc93apfs.js → chunk-etckvbrh.js} +17 -17
- package/dist/cli/{chunk-sc93apfs.js.map → chunk-etckvbrh.js.map} +2 -2
- package/dist/cli/{chunk-bxwhtvw8.js → chunk-f260jdrn.js} +5 -5
- package/dist/cli/{chunk-74wfhv79.js → chunk-fnxgtjeq.js} +1 -1
- package/dist/cli/{chunk-ms9z66nz.js → chunk-gwy55cx3.js} +2 -2
- package/dist/cli/{chunk-ms9z66nz.js.map → chunk-gwy55cx3.js.map} +2 -2
- package/dist/cli/{chunk-dhwtqnnd.js → chunk-gxhcf2hs.js} +2 -2
- package/dist/cli/{chunk-tfycddbt.js → chunk-h0qwdbem.js} +2 -2
- package/dist/cli/{chunk-nbyvgnnm.js → chunk-hktq4j5r.js} +109 -49
- package/dist/cli/chunk-hktq4j5r.js.map +36 -0
- package/dist/cli/{chunk-twy7psdz.js → chunk-hn3g2b89.js} +14 -10
- package/dist/cli/chunk-hn3g2b89.js.map +10 -0
- package/dist/cli/{chunk-j1x92mx9.js → chunk-j4nfgn2j.js} +1 -1
- package/dist/cli/{chunk-dpy8qdjf.js → chunk-k45sq16q.js} +74 -183
- package/dist/cli/chunk-k45sq16q.js.map +18 -0
- package/dist/cli/{chunk-5cmts6dv.js → chunk-kmswjxj9.js} +7 -7
- package/dist/cli/{chunk-5cmts6dv.js.map → chunk-kmswjxj9.js.map} +1 -1
- package/dist/cli/{chunk-36bxs9c7.js → chunk-nqna8dzp.js} +27 -23
- package/dist/cli/chunk-nqna8dzp.js.map +10 -0
- package/dist/cli/{chunk-a57yh0h9.js → chunk-p834rkcn.js} +7 -7
- package/dist/cli/{chunk-a57yh0h9.js.map → chunk-p834rkcn.js.map} +1 -1
- package/dist/cli/{chunk-60hz265r.js → chunk-ppa6mg4d.js} +24 -11
- package/dist/cli/chunk-ppa6mg4d.js.map +10 -0
- package/dist/cli/{chunk-s0bdnc7v.js → chunk-q1wwrxzw.js} +5 -4
- package/dist/cli/chunk-q1wwrxzw.js.map +10 -0
- package/dist/cli/{chunk-ave1wcbr.js → chunk-qcyxkj44.js} +1 -1
- package/dist/cli/{chunk-124fvmzq.js → chunk-r5nm9w0c.js} +11183 -8806
- package/dist/cli/{chunk-124fvmzq.js.map → chunk-r5nm9w0c.js.map} +82 -66
- package/dist/cli/{chunk-ckrcm08h.js → chunk-rtvgmg2j.js} +48 -62
- package/dist/cli/{chunk-ckrcm08h.js.map → chunk-rtvgmg2j.js.map} +4 -5
- package/dist/cli/{chunk-297ajdcv.js → chunk-t2h8pwy1.js} +6 -6
- package/dist/cli/{chunk-297ajdcv.js.map → chunk-t2h8pwy1.js.map} +3 -3
- package/dist/cli/{chunk-gxtg6ed2.js → chunk-x60e15st.js} +21 -21
- package/dist/cli/{chunk-gxtg6ed2.js.map → chunk-x60e15st.js.map} +3 -3
- package/dist/cli/{chunk-58bt3hag.js → chunk-x7bh6d9j.js} +4 -4
- package/dist/cli/{chunk-hvcxng5f.js → chunk-ydqzkhjn.js} +6 -6
- package/dist/cli/{chunk-kdpzw38a.js → chunk-yg8ckc3g.js} +95 -7
- package/dist/cli/chunk-yg8ckc3g.js.map +11 -0
- package/dist/cli/{chunk-0pmb4tr4.js → chunk-zxd5jrgx.js} +1205 -222
- package/dist/cli/chunk-zxd5jrgx.js.map +44 -0
- package/dist/cli/index.js +78 -42
- package/dist/cli/index.js.map +7 -6
- package/dist/types/ai/changelog-components.d.ts +18 -0
- package/dist/types/ai/changelog-markdown.d.ts +12 -0
- package/dist/types/ai/openapi-components.d.ts +7 -7
- package/dist/types/ai/operation-details.d.ts +39 -0
- package/dist/types/ai/relative-links.d.ts +3 -1
- package/dist/types/ai/serializers.d.ts +6 -4
- package/dist/types/analytics/head.d.ts +4 -2
- package/dist/types/analytics/script.d.ts +8 -2
- package/dist/types/components/content/youtube.d.ts +20 -0
- package/dist/types/components/layout/nav-utils.d.ts +8 -0
- package/dist/types/components/openapi/example-panels.d.ts +33 -0
- package/dist/types/components/openapi/helpers.d.ts +53 -11
- package/dist/types/components/openapi/snippets.d.ts +7 -1
- package/dist/types/core/alert-diagnostics.d.ts +10 -0
- package/dist/types/core/builtin-tags.d.ts +16 -0
- package/dist/types/core/changelog-list.d.ts +48 -0
- package/dist/types/core/closest-match.d.ts +6 -0
- package/dist/types/core/code-fence-diagnostics.d.ts +18 -0
- package/dist/types/core/code-fences.d.ts +8 -1
- package/dist/types/core/config-input.d.ts +38 -6
- package/dist/types/core/content-assets.d.ts +16 -18
- package/dist/types/core/data.d.ts +6 -2
- package/dist/types/core/diagnostics.d.ts +14 -0
- package/dist/types/core/directive-diagnostics.d.ts +22 -8
- package/dist/types/core/former-routes.d.ts +14 -0
- package/dist/types/core/github.d.ts +5 -4
- package/dist/types/core/heading-markers.d.ts +65 -2
- package/dist/types/core/hidden-pages.d.ts +3 -0
- package/dist/types/core/includes.d.ts +23 -0
- package/dist/types/core/last-modified.d.ts +18 -6
- package/dist/types/core/links.d.ts +42 -2
- package/dist/types/core/load-module.d.ts +19 -4
- package/dist/types/core/manifest.d.ts +14 -1
- package/dist/types/core/markdown-mirrors.d.ts +22 -0
- package/dist/types/core/mcp-clients.d.ts +47 -0
- package/dist/types/core/mdx-elements.d.ts +32 -0
- package/dist/types/core/mdx-syntax.d.ts +100 -0
- package/dist/types/core/meta-diagnostics.d.ts +7 -0
- package/dist/types/core/meta.d.ts +32 -5
- package/dist/types/core/nav-diagnostics.d.ts +16 -2
- package/dist/types/core/navigation.d.ts +13 -0
- package/dist/types/core/ordering-prefix.d.ts +6 -0
- package/dist/types/core/pinned-ids.d.ts +23 -0
- package/dist/types/core/project-graph.d.ts +9 -1
- package/dist/types/core/redirect-patterns.d.ts +18 -0
- package/dist/types/core/relative-files.d.ts +31 -0
- package/dist/types/core/schema.d.ts +322 -11
- package/dist/types/core/sources/normalize.d.ts +101 -9
- package/dist/types/core/sources/types.d.ts +16 -0
- package/dist/types/core/static-files.d.ts +10 -0
- package/dist/types/core/static-host.d.ts +42 -0
- package/dist/types/core/svg-images.d.ts +26 -0
- package/dist/types/core/syntax-diagnostics.d.ts +14 -0
- package/dist/types/core/types.d.ts +42 -1
- package/dist/types/deploy/generated-files.d.ts +41 -0
- package/dist/types/deploy/redirects.d.ts +21 -7
- package/dist/types/markdown/code-title.d.ts +9 -5
- package/dist/types/markdown/directives.d.ts +8 -0
- package/dist/types/markdown/fence-language.d.ts +51 -0
- package/dist/types/markdown/fence-meta.d.ts +5 -0
- package/dist/types/markdown/github-alerts.d.ts +35 -0
- package/dist/types/openapi/asyncapi.d.ts +6 -5
- package/dist/types/openapi/checks.d.ts +10 -5
- package/dist/types/openapi/model.d.ts +55 -8
- package/dist/types/openapi/parse.d.ts +4 -1
- package/dist/types/ratelimit/index.d.ts +2 -0
- package/dist/types/ratelimit/memory.d.ts +1 -1
- package/dist/types/ratelimit/schema.d.ts +40 -0
- package/dist/types/ratelimit/unkey.d.ts +48 -0
- package/dist/types/sources/filesystem.d.ts +14 -4
- package/dist/types/sources/mdx-remote.d.ts +6 -3
- package/dist/types/sources/registry.d.ts +3 -3
- package/dist/types/theme/color.d.ts +33 -0
- package/dist/types/theme/palette.d.ts +122 -0
- package/docs/01-quickstart.mdx +1 -1
- package/docs/02-deployment.mdx +24 -8
- package/docs/04-migrating.mdx +16 -4
- package/docs/advanced/changelog.mdx +27 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +1 -1
- package/docs/cli/doctor.mdx +2 -2
- package/docs/cli/evals.mdx +1 -1
- package/docs/cli/index.mdx +4 -2
- package/docs/cli/validate.mdx +10 -7
- package/docs/configuration/analytics.mdx +2 -0
- package/docs/configuration/customization.mdx +3 -1
- package/docs/configuration/index.mdx +7 -6
- package/docs/configuration/rate-limiting.mdx +20 -1
- package/docs/configuration/theming.mdx +5 -1
- package/docs/content/components.mdx +26 -6
- package/docs/content/frontmatter.mdx +1 -1
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/includes.mdx +6 -2
- package/docs/content/index.mdx +11 -1
- package/docs/content/islands.mdx +2 -0
- package/docs/content/meta.mdx +9 -3
- package/docs/content/navigation.mdx +14 -6
- package/docs/content/sources/obsidian.mdx +1 -1
- package/docs/content/sources/remote-mdx.mdx +3 -1
- package/docs/content/syntax.mdx +76 -8
- package/docs/content/variables.mdx +3 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/json-api.mdx +1 -1
- package/docs/discoverability/llms-txt.mdx +1 -1
- package/docs/discoverability/markdown.mdx +2 -2
- package/docs/discoverability/mcp.mdx +40 -8
- package/docs/discoverability/open-graph.mdx +1 -1
- package/docs/discoverability/sitemap-and-robots.mdx +1 -1
- package/docs/references/graphql.mdx +2 -0
- package/docs/references/openapi.mdx +45 -7
- package/package.json +4 -4
- package/skills/blume/SKILL.md +2 -2
- package/skills/blume-migrate/SKILL.md +51 -29
- package/skills/blume-migrate/references/docsify.md +262 -0
- package/skills/blume-migrate/references/docus.md +298 -0
- package/skills/blume-migrate/references/docusaurus.md +5 -5
- package/skills/blume-migrate/references/fern.md +327 -0
- package/skills/blume-migrate/references/fumadocs.md +1 -1
- package/skills/blume-migrate/references/gitbook.md +267 -0
- package/skills/blume-migrate/references/github-wiki.md +259 -0
- package/skills/blume-migrate/references/jekyll.md +253 -0
- package/skills/blume-migrate/references/mdbook.md +256 -0
- package/skills/blume-migrate/references/mintlify.md +5 -5
- package/skills/blume-migrate/references/mkdocs.md +236 -0
- package/skills/blume-migrate/references/monorepo.md +20 -8
- package/skills/blume-migrate/references/nextra.md +5 -5
- package/skills/blume-migrate/references/readme.md +257 -0
- package/skills/blume-migrate/references/redocly.md +198 -0
- package/skills/blume-migrate/references/starlight.md +3 -3
- package/skills/blume-migrate/references/vitepress.md +204 -0
- package/skills/blume-migrate/references/vuepress.md +271 -0
- package/skills/blume-migrate/scripts/docsify-codemod.mjs +2387 -0
- package/skills/blume-migrate/scripts/docus-codemod.mjs +2542 -0
- package/skills/blume-migrate/scripts/fern-codemod.mjs +2633 -0
- package/skills/blume-migrate/scripts/github-wiki-codemod.mjs +2808 -0
- package/skills/blume-migrate/scripts/include-excerpts.mjs +649 -0
- package/skills/blume-migrate/scripts/jekyll-codemod.mjs +3649 -0
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +17 -13
- package/skills/blume-migrate/scripts/mkdocs-codemod.mjs +2526 -0
- package/skills/blume-migrate/scripts/operation-routes.mjs +508 -0
- package/skills/blume-migrate/scripts/pin-heading-ids.mjs +1049 -0
- package/skills/blume-migrate/scripts/readme-codemod.mjs +4139 -0
- package/skills/blume-migrate/scripts/vitepress-codemod.mjs +1511 -0
- package/src/ai/changelog-components.ts +24 -0
- package/src/ai/changelog-markdown.ts +45 -41
- package/src/ai/component-markdown.ts +22 -9
- package/src/ai/llms.ts +36 -5
- package/src/ai/markdown.ts +2 -2
- package/src/ai/openapi-components.ts +25 -9
- package/src/ai/operation-details.ts +208 -0
- package/src/ai/relative-links.ts +48 -8
- package/src/ai/serializers.ts +7 -6
- package/src/ai/site-skill.ts +6 -2
- package/src/analytics/head.ts +11 -5
- package/src/analytics/script.ts +13 -3
- package/src/astro/generate.ts +47 -11
- package/src/astro/index.ts +1 -0
- package/src/astro/integration.ts +27 -1
- package/src/astro/mdx-source-errors.ts +46 -0
- package/src/astro/templates.ts +93 -189
- package/src/audit/checks/redirects.ts +5 -1
- package/src/audit/redirects.ts +36 -19
- package/src/audit/report.ts +22 -9
- package/src/audit/run.ts +12 -28
- package/src/audit/terms.ts +23 -0
- package/src/audit/url.ts +2 -2
- package/src/cli/build-failure.ts +256 -7
- package/src/cli/command-meta.ts +1 -1
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/build.ts +12 -3
- package/src/cli/commands/check.ts +13 -2
- package/src/cli/commands/dev.ts +1 -1
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/init.ts +3 -3
- package/src/cli/commands/preview.ts +17 -1
- package/src/cli/commands/validate.ts +22 -10
- package/src/cli/prepare.ts +1 -1
- package/src/cli/preview-pages.ts +161 -0
- package/src/cli/required-secrets.ts +0 -1
- package/src/cli/unknown-flags.ts +1 -42
- package/src/components/code-copy.ts +51 -0
- package/src/components/code-language.ts +75 -0
- package/src/components/content/AccordionItem.astro +16 -1
- package/src/components/content/Callout.astro +11 -1
- package/src/components/content/Changelog.astro +189 -0
- package/src/components/content/CodeBlock.astro +8 -11
- package/src/components/content/Tabs.astro +16 -2
- package/src/components/content/YouTube.astro +6 -4
- package/src/components/content/code-block.ts +33 -0
- package/src/components/content/content-strings.ts +5 -1
- package/src/components/content/inline-markdown.ts +35 -8
- package/src/components/content/youtube.ts +49 -7
- package/src/components/layout/Analytics.astro +5 -1
- package/src/components/layout/NavTree.astro +8 -3
- package/src/components/layout/PageActions.astro +81 -23
- package/src/components/layout/RootLayout.astro +35 -32
- package/src/components/layout/SiteFooter.astro +31 -8
- package/src/components/layout/nav-utils.ts +14 -0
- package/src/components/layout/search/pagefind.ts +2 -1
- package/src/components/openapi/Operation.astro +4 -0
- package/src/components/openapi/PanelTabs.astro +9 -3
- package/src/components/openapi/RequestBody.astro +42 -8
- package/src/components/openapi/RequestPanel.astro +34 -53
- package/src/components/openapi/example-panels.ts +110 -0
- package/src/components/openapi/helpers.ts +195 -20
- package/src/components/openapi/operation-model.ts +63 -26
- package/src/components/openapi/request.ts +13 -0
- package/src/components/openapi/snippets.ts +14 -2
- package/src/core/alert-diagnostics.ts +89 -0
- package/src/core/builtin-tags.ts +76 -0
- package/src/core/changelog-list.ts +87 -0
- package/src/core/closest-match.ts +41 -0
- package/src/core/code-fence-diagnostics.ts +267 -0
- package/src/core/code-fences.ts +43 -7
- package/src/core/component-diagnostics.ts +65 -12
- package/src/core/config-input.ts +39 -5
- package/src/core/config.ts +46 -1
- package/src/core/content-assets.ts +64 -144
- package/src/core/data.ts +10 -3
- package/src/core/diagnostics.ts +36 -3
- package/src/core/directive-diagnostics.ts +265 -47
- package/src/core/former-routes.ts +46 -0
- package/src/core/git-ignored.ts +53 -0
- package/src/core/github.ts +5 -4
- package/src/core/heading-markers.ts +156 -2
- package/src/core/hidden-pages.ts +64 -0
- package/src/core/includes.ts +181 -41
- package/src/core/last-modified.ts +47 -10
- package/src/core/links.ts +438 -83
- package/src/core/load-module.ts +98 -9
- package/src/core/manifest.ts +12 -9
- package/src/core/markdown-mirrors.ts +52 -0
- package/src/core/mcp-clients.ts +87 -0
- package/src/core/mdx-elements.ts +153 -0
- package/src/core/mdx-syntax.ts +377 -0
- package/src/core/meta-diagnostics.ts +134 -0
- package/src/core/meta.ts +154 -55
- package/src/core/nav-diagnostics.ts +41 -4
- package/src/core/navigation.ts +70 -10
- package/src/core/ordering-prefix.ts +22 -2
- package/src/core/pinned-ids.ts +141 -0
- package/src/core/project-graph.ts +221 -29
- package/src/core/redirect-patterns.ts +41 -0
- package/src/core/relative-files.ts +145 -0
- package/src/core/schema.ts +110 -6
- package/src/core/sources/mdx-remote.ts +75 -14
- package/src/core/sources/normalize.ts +885 -179
- package/src/core/sources/notion.ts +5 -4
- package/src/core/sources/obsidian.ts +2 -5
- package/src/core/sources/types.ts +17 -0
- package/src/core/static-files.ts +40 -0
- package/src/core/static-host.ts +125 -0
- package/src/core/svg-images.ts +98 -0
- package/src/core/syntax-diagnostics.ts +491 -0
- package/src/core/types.ts +43 -1
- package/src/deploy/artifacts.ts +2 -28
- package/src/deploy/generated-files.ts +128 -0
- package/src/deploy/platforms/vercel.ts +25 -1
- package/src/deploy/redirects.ts +43 -16
- package/src/deploy/sitemap.ts +2 -1
- package/src/markdown/base-links.ts +87 -6
- package/src/markdown/code-title.ts +29 -13
- package/src/markdown/content-assets.ts +210 -0
- package/src/markdown/directives.ts +69 -5
- package/src/markdown/fence-language.ts +106 -0
- package/src/markdown/fence-meta.ts +42 -0
- package/src/markdown/github-alerts.ts +172 -0
- package/src/markdown/heading-anchors.ts +265 -56
- package/src/markdown/include.ts +30 -3
- package/src/markdown/index.ts +145 -40
- package/src/markdown/prompt-text.ts +95 -0
- package/src/markdown/relative-links.ts +37 -2
- package/src/markdown/route-snapshot.ts +22 -12
- package/src/markdown/svg-fallback.ts +51 -0
- package/src/markdown/title-heading.ts +134 -0
- package/src/migrate/migrate.ts +127 -2
- package/src/og/card.ts +14 -4
- package/src/openapi/asyncapi.ts +14 -6
- package/src/openapi/checks.ts +160 -15
- package/src/openapi/model.ts +172 -33
- package/src/openapi/parse.ts +13 -4
- package/src/openapi/render-mdx.ts +55 -9
- package/src/openapi/source.ts +87 -6
- package/src/ratelimit/index.ts +2 -0
- package/src/ratelimit/memory.ts +1 -1
- package/src/ratelimit/runtime.ts +81 -0
- package/src/ratelimit/schema.ts +2 -0
- package/src/ratelimit/unkey.ts +61 -0
- package/src/registry/eject.ts +0 -1
- package/src/registry/registry.ts +1 -1
- package/src/search/documents.ts +10 -2
- package/src/sources/filesystem.ts +28 -3
- package/src/sources/mdx-remote.ts +7 -15
- package/src/theme/color.ts +14 -0
- package/src/theme/entry.ts +22 -1
- package/src/theme/palette.ts +74 -1
- package/dist/cli/chunk-0pmb4tr4.js.map +0 -35
- package/dist/cli/chunk-0w6hs479.js.map +0 -10
- package/dist/cli/chunk-150jk7km.js +0 -52
- package/dist/cli/chunk-150jk7km.js.map +0 -10
- package/dist/cli/chunk-36bxs9c7.js.map +0 -10
- package/dist/cli/chunk-53jqw8z0.js +0 -89
- package/dist/cli/chunk-53jqw8z0.js.map +0 -10
- package/dist/cli/chunk-60hz265r.js.map +0 -10
- package/dist/cli/chunk-desvm72z.js.map +0 -14
- package/dist/cli/chunk-dpy8qdjf.js.map +0 -17
- package/dist/cli/chunk-es8h0v3j.js +0 -441
- package/dist/cli/chunk-es8h0v3j.js.map +0 -11
- package/dist/cli/chunk-kdpzw38a.js.map +0 -11
- package/dist/cli/chunk-nbyvgnnm.js.map +0 -36
- package/dist/cli/chunk-s0bdnc7v.js.map +0 -10
- package/dist/cli/chunk-twy7psdz.js.map +0 -10
- package/dist/cli/chunk-ydp3065p.js +0 -64
- package/dist/cli/chunk-ydp3065p.js.map +0 -10
- /package/dist/cli/{chunk-y4msrwm9.js.map → chunk-080mfch6.js.map} +0 -0
- /package/dist/cli/{chunk-9qzgg6mj.js.map → chunk-34wap3ed.js.map} +0 -0
- /package/dist/cli/{chunk-1f882900.js.map → chunk-c5bjxq8v.js.map} +0 -0
- /package/dist/cli/{chunk-bxwhtvw8.js.map → chunk-f260jdrn.js.map} +0 -0
- /package/dist/cli/{chunk-74wfhv79.js.map → chunk-fnxgtjeq.js.map} +0 -0
- /package/dist/cli/{chunk-dhwtqnnd.js.map → chunk-gxhcf2hs.js.map} +0 -0
- /package/dist/cli/{chunk-tfycddbt.js.map → chunk-h0qwdbem.js.map} +0 -0
- /package/dist/cli/{chunk-j1x92mx9.js.map → chunk-j4nfgn2j.js.map} +0 -0
- /package/dist/cli/{chunk-ave1wcbr.js.map → chunk-qcyxkj44.js.map} +0 -0
- /package/dist/cli/{chunk-58bt3hag.js.map → chunk-x7bh6d9j.js.map} +0 -0
- /package/dist/cli/{chunk-hvcxng5f.js.map → chunk-ydqzkhjn.js.map} +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
// The changelog index's release list: every visible `type: changelog` entry,
|
|
3
|
+
// newest first, grouped by year, each row linking to its own page.
|
|
4
|
+
//
|
|
5
|
+
// The generated `/changelog` page renders this, and so can a hand-written page
|
|
6
|
+
// that wants prose above the list — an introduction, a feed link — which the
|
|
7
|
+
// generated page has no slot for. Only the list lives here: the heading and the
|
|
8
|
+
// description belong to whoever owns the page, so an authored page writes its
|
|
9
|
+
// own and the generated page keeps the translatable `changelog` strings.
|
|
10
|
+
import { getCollection } from "astro:content";
|
|
11
|
+
import data from "blume:data";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
compareChangelogDates,
|
|
15
|
+
groupChangelogYears,
|
|
16
|
+
parseChangelogDate,
|
|
17
|
+
uniqueAnchorIds,
|
|
18
|
+
} from "../../core/changelog-list.ts";
|
|
19
|
+
import { resolveDateFormatOptions } from "../../core/date-format.ts";
|
|
20
|
+
import { withMountedBase } from "../islands/base-path.ts";
|
|
21
|
+
import { contentStrings } from "./content-strings.ts";
|
|
22
|
+
|
|
23
|
+
const i18n = data.config.i18n;
|
|
24
|
+
// The list is always the default locale's entries, whatever locale the page
|
|
25
|
+
// sits in. Every other surface that publishes this list already works that way
|
|
26
|
+
// — the generated page, the `/changelog.md` mirror, llms-full.txt, MCP
|
|
27
|
+
// `get_page`, search — so a translated page listing its own locale's entries
|
|
28
|
+
// would be the one surface disagreeing with the rest about which releases
|
|
29
|
+
// exist. Releases are also commonly written once and left untranslated, in
|
|
30
|
+
// which case a per-locale list is the same list with fallback copies anyway.
|
|
31
|
+
// (The chrome around it still follows the reader: `strings` and the dates.)
|
|
32
|
+
const locale = i18n ? i18n.defaultLocale : null;
|
|
33
|
+
const { changelog: strings } = contentStrings(data, Astro.currentLocale);
|
|
34
|
+
// The page's own language, for the row dates and year headings. On the
|
|
35
|
+
// unprefixed generated `/changelog` Astro resolves the default locale.
|
|
36
|
+
const readerLocale = i18n ? (Astro.currentLocale ?? i18n.defaultLocale) : "en";
|
|
37
|
+
|
|
38
|
+
const dateFormatOptions = resolveDateFormatOptions(data.config.dateFormat);
|
|
39
|
+
// The year heads each group, so a row shows the rest of the date: a preset
|
|
40
|
+
// style keeps its month wording, a component format simply drops the year.
|
|
41
|
+
const { dateStyle, year: _year, ...dateComponents } = dateFormatOptions;
|
|
42
|
+
const rowDateFormat: Intl.DateTimeFormatOptions = dateStyle
|
|
43
|
+
? {
|
|
44
|
+
...dateComponents,
|
|
45
|
+
day: "numeric",
|
|
46
|
+
// `medium` abbreviates the month; every other preset spells it out, so a
|
|
47
|
+
// `long` site keeps reading "October 5" rather than dropping to "Oct 5".
|
|
48
|
+
month: dateStyle === "medium" ? "short" : "long",
|
|
49
|
+
}
|
|
50
|
+
: dateComponents;
|
|
51
|
+
const formatRowDate = (date: Date) =>
|
|
52
|
+
new Intl.DateTimeFormat(readerLocale, rowDateFormat).format(date);
|
|
53
|
+
const formatYear = (date: Date) =>
|
|
54
|
+
new Intl.DateTimeFormat(readerLocale, {
|
|
55
|
+
timeZone: dateFormatOptions.timeZone,
|
|
56
|
+
year: "numeric",
|
|
57
|
+
}).format(date);
|
|
58
|
+
|
|
59
|
+
const slugify = (text: string) =>
|
|
60
|
+
text
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
.replaceAll(/[^a-z0-9]+/gu, "-")
|
|
63
|
+
.replaceAll(/^-+|-+$/gu, "") || "update";
|
|
64
|
+
|
|
65
|
+
// Map each entry to its own page. The collection entry id matches the route
|
|
66
|
+
// manifest's `entryId`, and the manifest carries a route per locale and per
|
|
67
|
+
// archived version, all sharing that id — so a map over every route would keep
|
|
68
|
+
// whichever came last: a Portuguese permalink on an unlocalized index, or a
|
|
69
|
+
// row pointing into last year's archived docs. Keep the current docs of the
|
|
70
|
+
// listed locale, never a fallback copy (which renders another locale's content
|
|
71
|
+
// at this URL), and list an entry only when it has such a route, so a
|
|
72
|
+
// translated changelog file does not add a second row for the same release.
|
|
73
|
+
// These are the conditions `ai/changelog-markdown.ts` filters pages by, so the
|
|
74
|
+
// rendered list and the Markdown mirror select the same releases.
|
|
75
|
+
//
|
|
76
|
+
// Keyed by collection and id together, the way the catch-all looks an entry up:
|
|
77
|
+
// both collections id an entry by its file path, so a `releases/v1-0-0.md` on
|
|
78
|
+
// disk and a staged release from a source named `releases` share an id while
|
|
79
|
+
// rendering at different routes.
|
|
80
|
+
const entryKey = (collection: string, id: string) => `${collection}:${id}`;
|
|
81
|
+
const routeByEntry = new Map<string, string>();
|
|
82
|
+
for (const route of data.routes) {
|
|
83
|
+
if (
|
|
84
|
+
route.version === "" &&
|
|
85
|
+
!route.fallback &&
|
|
86
|
+
(locale === null || route.locale === locale)
|
|
87
|
+
) {
|
|
88
|
+
routeByEntry.set(entryKey(route.collection, route.entryId), route.path);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Non-filesystem changelog sources (GitHub Releases) render through a parallel
|
|
93
|
+
// `staged` collection, which exists only when such a source is configured — the
|
|
94
|
+
// route manifest says whether to look, so the index folds those releases in
|
|
95
|
+
// without a generator flag, and a page that reads only `docs` can't quietly
|
|
96
|
+
// drop them.
|
|
97
|
+
const hasStaged = data.routes.some((route) => route.collection === "staged");
|
|
98
|
+
const entries = [
|
|
99
|
+
...(await getCollection("docs")),
|
|
100
|
+
...(hasStaged ? await getCollection("staged" as "docs") : []),
|
|
101
|
+
].filter(
|
|
102
|
+
(entry) =>
|
|
103
|
+
entry.data.type === "changelog" &&
|
|
104
|
+
!entry.data.draft &&
|
|
105
|
+
!entry.data.sidebar?.hidden &&
|
|
106
|
+
// A route is required on every site, not only localized ones: without
|
|
107
|
+
// i18n the map still drops archived versions, and an entry with no
|
|
108
|
+
// current-docs route would otherwise be listed with a dead self-anchor
|
|
109
|
+
// while the Markdown mirror left it out.
|
|
110
|
+
routeByEntry.has(entryKey(entry.collection, entry.id))
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const rows = entries
|
|
114
|
+
.map((entry) => {
|
|
115
|
+
const date = parseChangelogDate(entry.data.date ?? entry.data.changelog?.date);
|
|
116
|
+
const label =
|
|
117
|
+
entry.data.title ??
|
|
118
|
+
(entry.data.changelog?.version
|
|
119
|
+
? `v${entry.data.changelog.version}`
|
|
120
|
+
: "Update");
|
|
121
|
+
const route = routeByEntry.get(entryKey(entry.collection, entry.id));
|
|
122
|
+
return {
|
|
123
|
+
date,
|
|
124
|
+
href: route ? withMountedBase(route) : null,
|
|
125
|
+
label,
|
|
126
|
+
tag: entry.data.changelog?.category ?? null,
|
|
127
|
+
};
|
|
128
|
+
})
|
|
129
|
+
.toSorted((a, b) => compareChangelogDates(a.date, b.date));
|
|
130
|
+
|
|
131
|
+
const ids = uniqueAnchorIds(rows.map((row) => slugify(row.label)));
|
|
132
|
+
const items = rows.map((row, index) => ({
|
|
133
|
+
date: row.date ? formatRowDate(row.date) : null,
|
|
134
|
+
dateTime: row.date ? row.date.toISOString().slice(0, 10) : null,
|
|
135
|
+
href: row.href,
|
|
136
|
+
id: ids[index] as string,
|
|
137
|
+
label: row.label,
|
|
138
|
+
tag: row.tag,
|
|
139
|
+
year: row.date ? formatYear(row.date) : null,
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
const groups = groupChangelogYears(items, (item) => item.year);
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
{
|
|
146
|
+
items.length === 0 ? (
|
|
147
|
+
<p>{strings.empty}</p>
|
|
148
|
+
) : (
|
|
149
|
+
<div class="not-prose mt-10 divide-y divide-border border-border border-y">
|
|
150
|
+
{groups.map((group) => (
|
|
151
|
+
<section
|
|
152
|
+
aria-label={group.year ?? undefined}
|
|
153
|
+
class="grid md:grid-cols-[6rem_minmax(0,1fr)] md:gap-x-8"
|
|
154
|
+
>
|
|
155
|
+
<h2 class="mt-0! pt-3! font-medium! text-muted-foreground text-sm! leading-5! tabular-nums max-md:pb-1">
|
|
156
|
+
{group.year}
|
|
157
|
+
</h2>
|
|
158
|
+
<ul class="m-0! list-none divide-y divide-border p-0!">
|
|
159
|
+
{group.items.map((item) => (
|
|
160
|
+
<li class="m-0! p-0!" id={item.id}>
|
|
161
|
+
<a
|
|
162
|
+
class="group/release flex items-baseline gap-4 py-3 no-underline! hover:no-underline!"
|
|
163
|
+
href={item.href ?? `#${item.id}`}
|
|
164
|
+
>
|
|
165
|
+
<span class="min-w-0 flex-1 font-medium text-foreground text-sm transition-colors group-hover/release:text-accent">
|
|
166
|
+
{item.label}
|
|
167
|
+
</span>
|
|
168
|
+
{item.tag && (
|
|
169
|
+
<span class="shrink-0 rounded-full bg-muted px-2 py-0.5 font-medium text-[0.65rem] text-muted-foreground">
|
|
170
|
+
{item.tag}
|
|
171
|
+
</span>
|
|
172
|
+
)}
|
|
173
|
+
{item.date && (
|
|
174
|
+
<time
|
|
175
|
+
class="shrink-0 font-mono text-muted-foreground text-xs tabular-nums"
|
|
176
|
+
datetime={item.dateTime}
|
|
177
|
+
>
|
|
178
|
+
{item.date}
|
|
179
|
+
</time>
|
|
180
|
+
)}
|
|
181
|
+
</a>
|
|
182
|
+
</li>
|
|
183
|
+
))}
|
|
184
|
+
</ul>
|
|
185
|
+
</section>
|
|
186
|
+
))}
|
|
187
|
+
</div>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
@@ -9,14 +9,16 @@
|
|
|
9
9
|
// The theme's code styles are scoped to `.prose`, so the output is wrapped in a
|
|
10
10
|
// `.prose` container (`max-w-none` so it fills its own column rather than the
|
|
11
11
|
// prose reading measure); that's what makes the light/dark swap work on a bare
|
|
12
|
-
// page without the manual `[data-theme]` CSS it would otherwise need.
|
|
12
|
+
// page without the manual `[data-theme]` CSS it would otherwise need. Without
|
|
13
|
+
// `code`, the children render as written: a fenced block wrapped in
|
|
14
|
+
// `<CodeBlock>` (Fern's and Mintlify's spelling) is already highlighted.
|
|
13
15
|
import data from "blume:data";
|
|
14
16
|
|
|
15
|
-
import {
|
|
17
|
+
import { codeBlockHtml } from "./code-block.ts";
|
|
16
18
|
|
|
17
19
|
interface Props {
|
|
18
|
-
/** The source to highlight. */
|
|
19
|
-
code
|
|
20
|
+
/** The source to highlight; without it, the children render instead. */
|
|
21
|
+
code?: string;
|
|
20
22
|
/** Shiki language id (default `txt`); an unknown one renders plain. */
|
|
21
23
|
lang?: string;
|
|
22
24
|
/** Show the brand language icon in the header (default on, like fences). */
|
|
@@ -25,12 +27,7 @@ interface Props {
|
|
|
25
27
|
title?: string;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
const
|
|
29
|
-
const html = await highlightCode(code.replace(/\n+$/u, ""), lang, {
|
|
30
|
-
icons,
|
|
31
|
-
themes: data.config.codeThemes,
|
|
32
|
-
title,
|
|
33
|
-
});
|
|
30
|
+
const html = await codeBlockHtml(Astro.props, data.config.codeThemes);
|
|
34
31
|
---
|
|
35
32
|
|
|
36
|
-
<div class="prose max-w-none"><Fragment set:html={html} /></div>
|
|
33
|
+
{html === null ? <slot /> : <div class="prose max-w-none"><Fragment set:html={html} /></div>}
|
|
@@ -110,6 +110,8 @@ const { content: strings } = contentStrings(data, Astro.currentLocale);
|
|
|
110
110
|
</blume-tabs>
|
|
111
111
|
|
|
112
112
|
<script>
|
|
113
|
+
import { languageLabel } from "../code-language.ts";
|
|
114
|
+
|
|
113
115
|
const TRIGGER_CLASS =
|
|
114
116
|
"-mb-px inline-flex shrink-0 cursor-pointer items-center gap-1.5 border-transparent border-b-2 bg-transparent px-1 py-2.5 font-medium text-muted-foreground text-xs transition-colors hover:text-foreground aria-[selected=true]:border-accent aria-[selected=true]:text-foreground";
|
|
115
117
|
const SELECT_CLASS =
|
|
@@ -120,9 +122,20 @@ const { content: strings } = contentStrings(data, Astro.currentLocale);
|
|
|
120
122
|
// in several <Tabs> instances on one page.
|
|
121
123
|
let instanceCount = 0;
|
|
122
124
|
|
|
123
|
-
// An untitled
|
|
125
|
+
// An untitled code block (a CodeGroup fence) is named by its language, read
|
|
126
|
+
// from the fence's own id: the docs layout moves that to
|
|
127
|
+
// `data-blume-language` when it relabels `data-language` for the header.
|
|
128
|
+
const codeLanguage = (panel: HTMLElement) => {
|
|
129
|
+
const language = panel.matches("pre")
|
|
130
|
+
? (panel.dataset.blumeLanguage ?? panel.dataset.language)
|
|
131
|
+
: undefined;
|
|
132
|
+
return language ? languageLabel(language) : undefined;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Any other untitled panel is numbered with its group's localized `Tab {n}`.
|
|
124
136
|
const panelTitle = (panel: HTMLElement, index: number) =>
|
|
125
137
|
panel.dataset.title ??
|
|
138
|
+
codeLanguage(panel) ??
|
|
126
139
|
(
|
|
127
140
|
panel.closest<HTMLElement>("blume-tabs")?.dataset.i18nTab ?? "Tab {n}"
|
|
128
141
|
).replace("{n}", String(index + 1));
|
|
@@ -160,7 +173,8 @@ const { content: strings } = contentStrings(data, Astro.currentLocale);
|
|
|
160
173
|
).filter((panel) => panel.closest("blume-tabs") === this);
|
|
161
174
|
// CodeGroup passes raw code fences instead of <Tab> elements, so adopt
|
|
162
175
|
// the content wrapper's direct code blocks as panels. The tab label comes
|
|
163
|
-
// from each block's title (the text after the language in the fence)
|
|
176
|
+
// from each block's title (the text after the language in the fence),
|
|
177
|
+
// or from its language when it has none.
|
|
164
178
|
// A ts2js fence arrives as its own TypeScript/JavaScript group; it is
|
|
165
179
|
// one panel too, labeled by the fence title both of its blocks carry.
|
|
166
180
|
if (panels.length === 0) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
import {
|
|
2
|
+
import { youtubeEmbedSrc } from "./youtube.ts";
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
5
|
/** A YouTube video id (e.g. `dQw4w9WgXcQ`). */
|
|
@@ -8,14 +8,16 @@ interface Props {
|
|
|
8
8
|
start?: number;
|
|
9
9
|
/** Accessible title for the embedded player. */
|
|
10
10
|
title?: string;
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* A full YouTube URL to extract the id from, as an alternative to `id`; a
|
|
13
|
+
* playlist URL embeds the playlist.
|
|
14
|
+
*/
|
|
12
15
|
url?: string;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
const { id, start, title = "YouTube video player", url } = Astro.props;
|
|
16
19
|
|
|
17
|
-
const
|
|
18
|
-
const src = videoId ? youtubeEmbedUrl(videoId, { start }) : null;
|
|
20
|
+
const src = youtubeEmbedSrc(id ?? url ?? "", { start });
|
|
19
21
|
---
|
|
20
22
|
|
|
21
23
|
{
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The highlighting half of the `<CodeBlock>` content component, kept in a
|
|
3
|
+
* sibling `.ts` (like `badge-color.ts`) so it is unit-testable.
|
|
4
|
+
*/
|
|
5
|
+
import { highlightCode } from "../../markdown/index.ts";
|
|
6
|
+
import type { CodeThemes } from "../../markdown/index.ts";
|
|
7
|
+
|
|
8
|
+
/** The props `<CodeBlock>` highlights from. */
|
|
9
|
+
export interface CodeBlockSource {
|
|
10
|
+
code?: string;
|
|
11
|
+
icons?: boolean;
|
|
12
|
+
lang?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The highlighted HTML for a `<CodeBlock>`, or null when it has no `code`.
|
|
18
|
+
* Fern and Mintlify wrap a fenced block in `<CodeBlock>…</CodeBlock>`; the
|
|
19
|
+
* fence inside is already highlighted by the Markdown pipeline, so a
|
|
20
|
+
* `CodeBlock` without `code` renders its children as written rather than
|
|
21
|
+
* failing the page on the missing string.
|
|
22
|
+
*/
|
|
23
|
+
export const codeBlockHtml = async (
|
|
24
|
+
{ code, icons, lang = "txt", title }: CodeBlockSource,
|
|
25
|
+
themes?: CodeThemes
|
|
26
|
+
): Promise<string | null> =>
|
|
27
|
+
code === undefined
|
|
28
|
+
? null
|
|
29
|
+
: await highlightCode(code.replace(/\n+$/u, ""), lang, {
|
|
30
|
+
icons,
|
|
31
|
+
themes,
|
|
32
|
+
title,
|
|
33
|
+
});
|
|
@@ -9,7 +9,10 @@ export interface ContentStringsData {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/** The UI string groups a content component's chrome reads. */
|
|
12
|
-
export type ContentStrings = Pick<
|
|
12
|
+
export type ContentStrings = Pick<
|
|
13
|
+
UIStrings,
|
|
14
|
+
"actions" | "changelog" | "content"
|
|
15
|
+
>;
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* Labels for a component rendered inside page content (MDX), which gets no
|
|
@@ -29,6 +32,7 @@ export const contentStrings = (
|
|
|
29
32
|
: data.ui;
|
|
30
33
|
return {
|
|
31
34
|
actions: { ...EN_UI.actions, ...dictionary.actions },
|
|
35
|
+
changelog: { ...EN_UI.changelog, ...dictionary.changelog },
|
|
32
36
|
content: { ...EN_UI.content, ...dictionary.content },
|
|
33
37
|
};
|
|
34
38
|
};
|
|
@@ -29,20 +29,47 @@ export const rawHtmlAsTextPlugin = defineMdastPlugin({
|
|
|
29
29
|
* `<p>a</p>\n<p>b</p>` would "unwrap" to `a</p>\n<p>b` — unbalanced HTML
|
|
30
30
|
* injected via `set:html`.
|
|
31
31
|
*/
|
|
32
|
+
const SINGLE_PARAGRAPH = /^<p>(?<content>(?:(?!<\/p>)[\s\S])*)<\/p>$/u;
|
|
33
|
+
|
|
32
34
|
export const unwrapParagraph = (html: string): string => {
|
|
33
35
|
const trimmed = html.trim();
|
|
34
|
-
|
|
35
|
-
return match?.groups?.content ?? trimmed;
|
|
36
|
+
return SINGLE_PARAGRAPH.exec(trimmed)?.groups?.content ?? trimmed;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
* text (see {@link rawHtmlAsTextPlugin}) and a single paragraph unwrapped.
|
|
41
|
-
*/
|
|
42
|
-
export const renderInlineMarkdown = async (value: string): Promise<string> => {
|
|
39
|
+
/** Render Markdown to HTML, its raw HTML shown as text. */
|
|
40
|
+
const renderMarkdown = async (value: string): Promise<string> => {
|
|
43
41
|
const processor = await createSatteriMarkdownProcessor({
|
|
44
42
|
mdastPlugins: [rawHtmlAsTextPlugin],
|
|
45
43
|
});
|
|
46
44
|
const rendered = await processor.render(value);
|
|
47
|
-
return
|
|
45
|
+
return rendered.code;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Render a short Markdown text prop to inline HTML, its raw HTML shown as
|
|
50
|
+
* text (see {@link rawHtmlAsTextPlugin}) and a single paragraph unwrapped.
|
|
51
|
+
*/
|
|
52
|
+
export const renderInlineMarkdown = async (value: string): Promise<string> =>
|
|
53
|
+
unwrapParagraph(await renderMarkdown(value));
|
|
54
|
+
|
|
55
|
+
// What inline Markdown a title can hold starts with: a backslash escape, a
|
|
56
|
+
// code span, emphasis or strikethrough, or a link.
|
|
57
|
+
const INLINE_SYNTAX = /[\\`*_~[]/u;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A title prop (a callout's, an accordion item's) as inline HTML, with its
|
|
61
|
+
* Markdown rendered — `` `code` ``, `**bold**`, `[links](/x)` — and its raw
|
|
62
|
+
* HTML shown as text. Undefined when the title renders as written instead:
|
|
63
|
+
* when it holds none of that syntax, so a plain title keeps the exact text it
|
|
64
|
+
* always rendered, and when it doesn't render as one paragraph (`1. Install`
|
|
65
|
+
* is no list).
|
|
66
|
+
*/
|
|
67
|
+
export const renderInlineTitle = async (
|
|
68
|
+
value: string
|
|
69
|
+
): Promise<string | undefined> => {
|
|
70
|
+
if (!INLINE_SYNTAX.test(value)) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const html = await renderMarkdown(value);
|
|
74
|
+
return SINGLE_PARAGRAPH.exec(html.trim())?.groups?.content;
|
|
48
75
|
};
|
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
const BARE_ID = /^[\w-]{11}$/u;
|
|
9
9
|
|
|
10
10
|
// Pull the id out of any common YouTube URL: youtu.be/<id>, watch?v=<id>,
|
|
11
|
-
// /embed/<id>, /shorts/<id>, /live/<id>.
|
|
11
|
+
// /embed/<id>, /shorts/<id>, /live/<id>. A playlist embed's
|
|
12
|
+
// `/embed/videoseries` is 11 characters too, but it names no video.
|
|
12
13
|
const URL_ID =
|
|
13
|
-
/(?:youtu\.be\/|\/embed
|
|
14
|
+
/(?:youtu\.be\/|\/embed\/(?!videoseries\b)|\/shorts\/|\/live\/|[?&]v=)(?<id>[\w-]{11})/u;
|
|
15
|
+
|
|
16
|
+
// A playlist URL: the embed form, `/embed/videoseries?list=<id>`, or the
|
|
17
|
+
// playlist page, `/playlist?list=<id>`.
|
|
18
|
+
const PLAYLIST =
|
|
19
|
+
/\/(?:embed\/videoseries|playlist)\?(?:[^#]*&)?list=(?<list>[\w-]+)/u;
|
|
14
20
|
|
|
15
21
|
/**
|
|
16
22
|
* Resolve a YouTube video id from either a bare id or a full URL. Returns `null`
|
|
@@ -28,6 +34,18 @@ export const parseYouTubeId = (input: string): string | null => {
|
|
|
28
34
|
return URL_ID.exec(value)?.groups?.id ?? null;
|
|
29
35
|
};
|
|
30
36
|
|
|
37
|
+
/**
|
|
38
|
+
* The playlist id of a YouTube playlist URL (`/embed/videoseries?list=…` or
|
|
39
|
+
* `/playlist?list=…`), or `null` for anything else — a video URL that also
|
|
40
|
+
* carries a `list` (`watch?v=…&list=…`) is that video.
|
|
41
|
+
*/
|
|
42
|
+
export const parseYouTubePlaylist = (input: string): string | null =>
|
|
43
|
+
PLAYLIST.exec(input.trim())?.groups?.list ?? null;
|
|
44
|
+
|
|
45
|
+
/** The query a `start` time adds to an embed URL, or nothing. */
|
|
46
|
+
const startParam = (start: number | undefined): Record<string, string> =>
|
|
47
|
+
start && start > 0 ? { start: String(Math.floor(start)) } : {};
|
|
48
|
+
|
|
31
49
|
/**
|
|
32
50
|
* Build a privacy-enhanced (`youtube-nocookie.com`) embed URL, optionally
|
|
33
51
|
* starting at `start` seconds.
|
|
@@ -37,10 +55,34 @@ export const youtubeEmbedUrl = (
|
|
|
37
55
|
options: { start?: number } = {}
|
|
38
56
|
): string => {
|
|
39
57
|
const base = `https://www.youtube-nocookie.com/embed/${id}`;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
const params = new URLSearchParams(startParam(options.start));
|
|
59
|
+
return params.size > 0 ? `${base}?${params.toString()}` : base;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The privacy-enhanced embed URL for a playlist, optionally starting its
|
|
64
|
+
* first video at `start` seconds.
|
|
65
|
+
*/
|
|
66
|
+
export const youtubePlaylistEmbedUrl = (
|
|
67
|
+
list: string,
|
|
68
|
+
options: { start?: number } = {}
|
|
69
|
+
): string => {
|
|
70
|
+
const params = new URLSearchParams({ list, ...startParam(options.start) });
|
|
71
|
+
return `https://www.youtube-nocookie.com/embed/videoseries?${params.toString()}`;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The embed URL for a `<YouTube>`'s `id` or `url`: a video, else a playlist,
|
|
76
|
+
* else `null` when the input names neither.
|
|
77
|
+
*/
|
|
78
|
+
export const youtubeEmbedSrc = (
|
|
79
|
+
input: string,
|
|
80
|
+
options: { start?: number } = {}
|
|
81
|
+
): string | null => {
|
|
82
|
+
const id = parseYouTubeId(input);
|
|
83
|
+
if (id) {
|
|
84
|
+
return youtubeEmbedUrl(id, options);
|
|
44
85
|
}
|
|
45
|
-
|
|
86
|
+
const list = parseYouTubePlaylist(input);
|
|
87
|
+
return list ? youtubePlaylistEmbedUrl(list, options) : null;
|
|
46
88
|
};
|
|
@@ -21,7 +21,11 @@ interface Props {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const { analytics } = Astro.props;
|
|
24
|
-
|
|
24
|
+
// A `script()` src in `public/` is served under the deployment base.
|
|
25
|
+
const nodes = analyticsHead(
|
|
26
|
+
import.meta.env.PROD ? (analytics ?? []) : [],
|
|
27
|
+
import.meta.env.BASE_URL
|
|
28
|
+
);
|
|
25
29
|
const held = data.config.consent
|
|
26
30
|
? { "data-blume-consent": "analytics", type: "text/plain" }
|
|
27
31
|
: {};
|
|
@@ -8,7 +8,7 @@ import { withBase, withMountedBase } from "../islands/base-path.ts";
|
|
|
8
8
|
import Self from "./NavTree.astro";
|
|
9
9
|
import NavTreeCache from "./NavTreeCache.astro";
|
|
10
10
|
import NavTreeScript from "./NavTreeScript.astro";
|
|
11
|
-
import { isGroupRowCurrent } from "./nav-utils.ts";
|
|
11
|
+
import { groupStartsOpen, isGroupRowCurrent } from "./nav-utils.ts";
|
|
12
12
|
|
|
13
13
|
interface PagePanel {
|
|
14
14
|
id: string;
|
|
@@ -388,8 +388,13 @@ const initialId =
|
|
|
388
388
|
if (display === "group") {
|
|
389
389
|
const active = hasActivePage(item);
|
|
390
390
|
// Default every group closed; open only those on the active path (so
|
|
391
|
-
// the current page's ancestors stay expanded)
|
|
392
|
-
|
|
391
|
+
// the current page's ancestors stay expanded), forced open via meta,
|
|
392
|
+
// or alone at the top of the sidebar.
|
|
393
|
+
const open = groupStartsOpen(
|
|
394
|
+
item,
|
|
395
|
+
active,
|
|
396
|
+
depth === 0 && items.length === 1
|
|
397
|
+
);
|
|
393
398
|
return (
|
|
394
399
|
<li class={spacing}>
|
|
395
400
|
<details open={open}>
|