diff --git a/astro.config.ts b/astro.config.ts index 1be914ecb79707..f0d9bf393ee5e1 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -4,71 +4,20 @@ import tailwind from "@astrojs/tailwind"; import starlightDocSearch from "@astrojs/starlight-docsearch"; import starlightImageZoom from "starlight-image-zoom"; import liveCode from "astro-live-code"; -import rehypeMermaid from "rehype-mermaid"; -import rehypeAutolinkHeadings, { - type Options as rehypeAutolinkHeadingsOptions, -} from "rehype-autolink-headings"; -import rehypeExternalLinks from "rehype-external-links"; import starlightLinksValidator from "starlight-links-validator"; -import { h } from "hastscript"; -import { readdir } from "fs/promises"; import icon from "astro-icon"; import sitemap from "@astrojs/sitemap"; import react from "@astrojs/react"; + import rehypeTitleFigure from "rehype-title-figure"; -import rehypeHeadingSlugs from "./plugins/rehype/heading-slugs"; +import rehypeMermaid from "./src/plugins/rehype/mermaid.ts"; +import rehypeAutolinkHeadings from "./src/plugins/rehype/autolink-headings.ts"; +import rehypeExternalLinks from "./src/plugins/rehype/external-links.ts"; +import rehypeHeadingSlugs from "./src/plugins/rehype/heading-slugs.ts"; -const runLinkCheck = process.env.RUN_LINK_CHECK || false; +import { sidebar } from "./src/util/sidebar.ts"; -async function autogenSections() { - const sections = ( - await readdir("./src/content/docs/", { - withFileTypes: true, - }) - ) - .filter((x) => x.isDirectory()) - .map((x) => x.name); - return sections.map((x) => { - return { - label: x, - autogenerate: { - directory: x, - collapsed: true, - }, - }; - }); -} -const AnchorLinkIcon = h( - "span", - { - ariaHidden: "true", - class: "anchor-icon", - }, - h( - "svg", - { - width: 16, - height: 16, - viewBox: "0 0 24 24", - }, - h("path", { - fill: "currentcolor", - d: "m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z", - }), - ), -); -const autolinkConfig: rehypeAutolinkHeadingsOptions = { - properties: { - class: "anchor-link", - }, - behavior: "after", - group: ({ tagName }) => - h("div", { - tabIndex: -1, - class: `heading-wrapper level-${tagName}`, - }), - content: () => [AnchorLinkIcon], -}; +const runLinkCheck = process.env.RUN_LINK_CHECK || false; // https://astro.build/config export default defineConfig({ @@ -76,28 +25,11 @@ export default defineConfig({ markdown: { smartypants: false, rehypePlugins: [ - [ - rehypeMermaid, - { - strategy: "pre-mermaid", - }, - ], - [ - rehypeExternalLinks, - { - content: { - type: "text", - value: " ↗", - }, - properties: { - target: "_blank", - }, - rel: ["noopener"], - }, - ], + rehypeMermaid, + rehypeExternalLinks, rehypeHeadingSlugs, - [rehypeAutolinkHeadings, autolinkConfig], - // @ts-expect-error TODO: fix types + rehypeAutolinkHeadings, + // @ts-expect-error plugins types are outdated but functional rehypeTitleFigure, ], }, @@ -116,29 +48,15 @@ export default defineConfig({ src: "./src/assets/logo.svg", }, favicon: "/favicon.png", - head: [ - { - tag: "meta", - attrs: { - name: "image", - content: "https://developers.cloudflare.com/cf-twitter-card.png", - }, - }, - { + head: ["image", "og:image", "twitter:image"].map((name) => { + return { tag: "meta", attrs: { - name: "og:image", + name, content: "https://developers.cloudflare.com/cf-twitter-card.png", }, - }, - { - tag: "meta", - attrs: { - name: "twitter:image", - content: "https://developers.cloudflare.com/cf-twitter-card.png", - }, - }, - ], + }; + }), social: { github: "https://github.com/cloudflare/cloudflare-docs", "x.com": "https://x.com/cloudflare", @@ -161,18 +79,17 @@ export default defineConfig({ SkipLink: "./src/components/overrides/SkipLink.astro", TableOfContents: "./src/components/overrides/TableOfContents.astro", }, - sidebar: await autogenSections(), + sidebar, customCss: [ "./src/asides.css", "./src/code.css", + "./src/footnotes.css", "./src/headings.css", "./src/input.css", - "./src/littlefoot.css", "./src/mermaid.css", "./src/table.css", "./src/tailwind.css", "./src/title.css", - "./src/tooltips.css", ], pagination: false, plugins: [ diff --git a/ec.config.mjs b/ec.config.mjs index 2d24023b4f653a..e68681f1ac852b 100644 --- a/ec.config.mjs +++ b/ec.config.mjs @@ -2,9 +2,9 @@ import darkTheme from "solarflare-theme/themes/cloudflare-dark-color-theme.json" with { type: "json" }; import lightTheme from "solarflare-theme/themes/cloudflare-light-color-theme.json" with { type: "json" }; -import pluginWorkersPlayground from "./plugins/expressive-code/workers-playground.js"; -import pluginOutputFrame from "./plugins/expressive-code/output-frame.js"; -import pluginDefaultTitles from "./plugins/expressive-code/default-titles.js"; +import pluginWorkersPlayground from "./src/plugins/expressive-code/workers-playground.js"; +import pluginOutputFrame from "./src/plugins/expressive-code/output-frame.js"; +import pluginDefaultTitles from "./src/plugins/expressive-code/default-titles.js"; import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections"; diff --git a/package-lock.json b/package-lock.json index dce2df925b16a8..3e95cc2df14de4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55,7 +55,6 @@ "mdast-util-mdx-expression": "2.0.1", "mermaid": "11.4.1", "node-html-parser": "7.0.1", - "playwright": "1.49.1", "prettier": "3.4.2", "prettier-plugin-astro": "0.14.1", "prettier-plugin-tailwindcss": "0.6.9", @@ -65,9 +64,9 @@ "react-dom": "19.0.0", "react-markdown": "9.0.3", "redirects-in-workers": "0.0.5", + "rehype": "13.0.2", "rehype-autolink-headings": "7.1.0", "rehype-external-links": "3.0.0", - "rehype-mermaid": "3.0.0", "rehype-title-figure": "0.1.2", "remark": "15.0.1", "sharp": "0.33.5", @@ -3186,16 +3185,6 @@ "node": ">=14" } }, - "node_modules/@fortawesome/fontawesome-free": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz", - "integrity": "sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==", - "dev": true, - "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", - "engines": { - "node": ">=6" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -11617,22 +11606,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-dom": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", - "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "@types/hast": "^3.0.0", - "hastscript": "^9.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/hast-util-from-html": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", @@ -11652,23 +11625,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-html-isomorphic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", - "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-dom": "^5.0.0", - "hast-util-from-html": "^2.0.0", - "unist-util-remove-position": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/hast-util-from-parse5": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.2.tgz", @@ -13897,28 +13853,6 @@ "uuid": "^9.0.1" } }, - "node_modules/mermaid-isomorphic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mermaid-isomorphic/-/mermaid-isomorphic-3.0.0.tgz", - "integrity": "sha512-6RBUQD0ZWzBHO4KZ8JMK3a/lNL7383N6K4nXzOdA2Ylnhz34qC8Nls2ZgOJVjGuB9Iq1bP61XKE0K/fNOD2n6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@fortawesome/fontawesome-free": "^6.0.0", - "mermaid": "^11.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/remcohaszing" - }, - "peerDependencies": { - "playwright": "1" - }, - "peerDependenciesMeta": { - "playwright": { - "optional": true - } - } - }, "node_modules/mermaid/node_modules/marked": { "version": "13.0.3", "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", @@ -14782,16 +14716,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mini-svg-data-uri": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", - "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", - "dev": true, - "license": "MIT", - "bin": { - "mini-svg-data-uri": "cli.js" - } - }, "node_modules/miniflare": { "version": "3.20241230.1", "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20241230.1.tgz", @@ -15913,38 +15837,6 @@ "dev": true, "license": "MIT" }, - "node_modules/playwright": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", - "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.49.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", - "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/points-on-curve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", @@ -17046,35 +16938,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-mermaid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rehype-mermaid/-/rehype-mermaid-3.0.0.tgz", - "integrity": "sha512-fxrD5E4Fa1WXUjmjNDvLOMT4XB1WaxcfycFIWiYU0yEMQhcTDElc9aDFnbDFRLxG1Cfo1I3mfD5kg4sjlWaB+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-html-isomorphic": "^2.0.0", - "hast-util-to-text": "^4.0.0", - "mermaid-isomorphic": "^3.0.0", - "mini-svg-data-uri": "^1.0.0", - "space-separated-tokens": "^2.0.0", - "unified": "^11.0.0", - "unist-util-visit-parents": "^6.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/remcohaszing" - }, - "peerDependencies": { - "playwright": "1" - }, - "peerDependenciesMeta": { - "playwright": { - "optional": true - } - } - }, "node_modules/rehype-parse": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", diff --git a/package.json b/package.json index 4a58ac67daceaf..a5d6d5a58b0c34 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ "mdast-util-mdx-expression": "2.0.1", "mermaid": "11.4.1", "node-html-parser": "7.0.1", - "playwright": "1.49.1", "prettier": "3.4.2", "prettier-plugin-astro": "0.14.1", "prettier-plugin-tailwindcss": "0.6.9", @@ -84,9 +83,9 @@ "react-dom": "19.0.0", "react-markdown": "9.0.3", "redirects-in-workers": "0.0.5", + "rehype": "13.0.2", "rehype-autolink-headings": "7.1.0", "rehype-external-links": "3.0.0", - "rehype-mermaid": "3.0.0", "rehype-title-figure": "0.1.2", "remark": "15.0.1", "sharp": "0.33.5", diff --git a/src/components/AnchorHeading.astro b/src/components/AnchorHeading.astro index e33fb6e58e58a7..8d9b9849cb0af7 100644 --- a/src/components/AnchorHeading.astro +++ b/src/components/AnchorHeading.astro @@ -3,6 +3,9 @@ import { z } from "astro:schema"; import { marked } from "marked"; import { slug as GithubSlug } from "github-slugger"; +import { rehype } from "rehype"; +import rehypeAutoLinkHeadings from "~/plugins/rehype/autolink-headings"; + type Props = z.infer; const props = z.object({ @@ -15,19 +18,13 @@ const { title, slug, depth } = props.parse(Astro.props); const slugified = GithubSlug(slug ?? title); -const Heading = `h${depth}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; +const tag = `h${depth}` as "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; + +const file = await rehype() + .use(rehypeAutoLinkHeadings) + .process(`<${tag} id=${slugified}>${marked.parseInline(title)}`); + +const html = file.toString(); --- -
- - - - - - - - -
+ diff --git a/src/components/overrides/Sidebar.astro b/src/components/overrides/Sidebar.astro index e470eccd9403c8..c94f3bfae50d31 100644 --- a/src/components/overrides/Sidebar.astro +++ b/src/components/overrides/Sidebar.astro @@ -7,6 +7,8 @@ import { getEntry } from "astro:content"; import { Badge } from "@astrojs/starlight/components"; import type { ComponentProps, HTMLAttributes } from "astro/types"; +import { rehypeExternalLinksOptions } from "~/plugins/rehype/external-links"; + const { sidebar, slug } = Astro.props; interface Link { @@ -134,7 +136,7 @@ async function handleLink(link: Link): Promise { if (frontmatter.external_link) { return { ...link, - label: link.label.concat(" ↗"), + label: link.label.concat(rehypeExternalLinksOptions.content.value), href: frontmatter.external_link, badge: frontmatter.external_link.startsWith("/api") ? { diff --git a/src/content/docs/style-guide/components/anchor-heading.mdx b/src/content/docs/style-guide/components/anchor-heading.mdx index 64b7a3d2110246..90b2ff1ab0c817 100644 --- a/src/content/docs/style-guide/components/anchor-heading.mdx +++ b/src/content/docs/style-guide/components/anchor-heading.mdx @@ -19,14 +19,9 @@ Markdown files (including partials) have this behavior by default, applied via r To override the ID given to a heading within Markdown, add an MDX comment at the end of the line: -```mdx -# foo {/*bar*/} -``` - -It will result in the following HTML: - -```html -foo +```mdx live +## foo {/*bar*/} +{/* HTML:

foo

*/} ``` :::note diff --git a/src/content/docs/style-guide/formatting/footnotes.mdx b/src/content/docs/style-guide/formatting/footnotes.mdx index 72117516a4565f..8a3b560070b227 100644 --- a/src/content/docs/style-guide/formatting/footnotes.mdx +++ b/src/content/docs/style-guide/formatting/footnotes.mdx @@ -5,6 +5,4 @@ title: Footnotes Footnotes are useful when you want to provide additional context about an item but that context would greatly distract from the flow of reading through the document (legal disclaimers, pricing, etc.). -We use [https://littlefoot.js.org/](https://littlefoot.js.org/), which helps us render fancy footnotes in context of what you are talking about (and also are much better for mobile). - To implement footnotes, use standard [markdown footnote formatting](https://github.blog/changelog/2021-09-30-footnotes-now-supported-in-markdown-fields/). You can also see an example in the [Load Balancing documentation](https://github.com/cloudflare/cloudflare-docs/blob/production/src/content/docs/load-balancing/additional-options/spectrum.mdx?plain=1#L42). diff --git a/src/footnotes.css b/src/footnotes.css new file mode 100644 index 00000000000000..c53ff3d6aafa13 --- /dev/null +++ b/src/footnotes.css @@ -0,0 +1,19 @@ +.footnote { + font-size: 0.75rem; + line-height: 1rem; + color: var(--sl-color-accent); + font-weight: 600; + padding: 0.25rem; + margin: -0.25rem; +} + +.data-footnote-backref { + display: none; +} + +.tippy-box { + background-color: var(--sl-color-bg-nav); + border-color: var(--sl-color-text); + border: 0.1em solid; + color: var(--sl-color-white); +} diff --git a/src/kbd.css b/src/kbd.css deleted file mode 100644 index 6dc8fc7ba9f973..00000000000000 --- a/src/kbd.css +++ /dev/null @@ -1,6 +0,0 @@ -kbd { - border: 1px solid rgb(251 146 60); - border-radius: 0.25rem; - background-color: rgb(251 146 60 / 0.1); - padding: 0.1rem; -} diff --git a/src/littlefoot.css b/src/littlefoot.css deleted file mode 100644 index c03bac3fd397da..00000000000000 --- a/src/littlefoot.css +++ /dev/null @@ -1,13 +0,0 @@ -.littlefoot { - --popover-font-family: var(--sl-font-system); - --popover-background-color: var(--sl-color-bg); - --popover-text-color: var(--sl-color-white); -} - -.littlefoot__content > p > a { - color: var(--sl-color-text-accent); -} - -.data-footnote-backref { - display: none; -} diff --git a/plugins/expressive-code/default-titles.js b/src/plugins/expressive-code/default-titles.js similarity index 100% rename from plugins/expressive-code/default-titles.js rename to src/plugins/expressive-code/default-titles.js diff --git a/plugins/expressive-code/output-frame.js b/src/plugins/expressive-code/output-frame.js similarity index 100% rename from plugins/expressive-code/output-frame.js rename to src/plugins/expressive-code/output-frame.js diff --git a/plugins/expressive-code/workers-playground.js b/src/plugins/expressive-code/workers-playground.js similarity index 100% rename from plugins/expressive-code/workers-playground.js rename to src/plugins/expressive-code/workers-playground.js diff --git a/src/plugins/rehype/autolink-headings.ts b/src/plugins/rehype/autolink-headings.ts new file mode 100644 index 00000000000000..4fd5baa6e047cf --- /dev/null +++ b/src/plugins/rehype/autolink-headings.ts @@ -0,0 +1,39 @@ +import rehypeAutolinkHeadings, { type Options } from "rehype-autolink-headings"; +import { h } from "hastscript"; + +export const rehypeAutolinkHeadingsOptions = { + properties: { + class: "anchor-link", + }, + behavior: "after", + group: ({ tagName }: { tagName: string }) => + h("div", { + tabIndex: -1, + class: `heading-wrapper level-${tagName}`, + }), + content: () => [AnchorLinkIcon], +} as const satisfies Options; + +const AnchorLinkIcon = h( + "span", + { + ariaHidden: "true", + class: "anchor-icon", + }, + h( + "svg", + { + width: 16, + height: 16, + viewBox: "0 0 24 24", + }, + h("path", { + fill: "currentcolor", + d: "m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z", + }), + ), +); + +export default function () { + return rehypeAutolinkHeadings(rehypeAutolinkHeadingsOptions); +} diff --git a/src/plugins/rehype/external-links.ts b/src/plugins/rehype/external-links.ts new file mode 100644 index 00000000000000..68039f18f0fc91 --- /dev/null +++ b/src/plugins/rehype/external-links.ts @@ -0,0 +1,19 @@ +import rehypeExternalLinks, { type Options } from "rehype-external-links"; + +export const rehypeExternalLinksOptions = { + content: { + type: "text", + value: " ↗", + }, + contentProperties: { + class: "external-link", + }, + properties: { + target: "_blank", + }, + rel: ["noopener"], +} as const satisfies Options; + +export default function () { + return rehypeExternalLinks(rehypeExternalLinksOptions); +} diff --git a/plugins/rehype/heading-slugs.ts b/src/plugins/rehype/heading-slugs.ts similarity index 86% rename from plugins/rehype/heading-slugs.ts rename to src/plugins/rehype/heading-slugs.ts index 232afaea962201..f20e250d175c42 100644 --- a/plugins/rehype/heading-slugs.ts +++ b/src/plugins/rehype/heading-slugs.ts @@ -1,6 +1,7 @@ import { toString } from "hast-util-to-string"; import { visit } from "unist-util-visit"; import GithubSlugger from "github-slugger"; +import { rehypeExternalLinksOptions } from "./external-links"; import type { Root } from "hast"; import type { MdxTextExpression } from "mdast-util-mdx-expression"; @@ -31,7 +32,9 @@ export default function () { } } else { if (!element.properties.id) { - const string = toString(element).replaceAll(" ↗", "").trimEnd(); + const string = toString(element) + .replaceAll(rehypeExternalLinksOptions.content.value, "") + .trimEnd(); element.properties.id = slugs.slug(string); } diff --git a/src/plugins/rehype/mermaid.ts b/src/plugins/rehype/mermaid.ts new file mode 100644 index 00000000000000..fd1d17d50c8711 --- /dev/null +++ b/src/plugins/rehype/mermaid.ts @@ -0,0 +1,89 @@ +/** + * Taken from https://github.com/remcohaszing/rehype-mermaid + * to only support the "pre-mermaid" strategy. + */ + +import { visitParents } from "unist-util-visit-parents"; +import type { Root, Element } from "hast"; +import { parse } from "space-separated-tokens"; +import { toText } from "hast-util-to-text"; + +interface CodeInstance { + diagram: string; + ancestors: Element[]; +} + +const nonWhitespacePattern = /\w/; + +function isMermaidElement(element: Element): boolean { + let mermaidClassName: string; + + if (element.tagName === "code") { + mermaidClassName = "language-mermaid"; + } else { + return false; + } + + let className = element.properties?.className; + if (typeof className === "string") { + className = parse(className); + } + + if (!Array.isArray(className)) { + return false; + } + + return className.includes(mermaidClassName); +} + +export default function () { + return function (tree: Root) { + const instances: CodeInstance[] = []; + + visitParents(tree, "element", (node, ancestors) => { + if (!isMermaidElement(node)) { + return; + } + + const parent = ancestors.at(-1)!; + let inclusiveAncestors = ancestors as Element[]; + + if (parent.type === "element" && parent.tagName === "pre") { + for (const child of parent.children) { + if (child.type === "text") { + if (nonWhitespacePattern.test(child.value)) { + return; + } + } else if (child !== node) { + return; + } + } + } else { + inclusiveAncestors = [...inclusiveAncestors, node]; + } + + instances.push({ + diagram: toText(node, { whitespace: "pre" }), + ancestors: inclusiveAncestors, + }); + }); + + if (!instances.length) { + return; + } + + for (const { ancestors, diagram } of instances) { + const parent = ancestors.at(-2)!; + const node = ancestors.at(-1)!; + + parent.children[parent.children.indexOf(node)] = { + type: "element", + tagName: "pre", + properties: { + className: ["mermaid"], + }, + children: [{ type: "text", value: diagram }], + }; + } + }; +} diff --git a/src/tooltips.css b/src/tooltips.css deleted file mode 100644 index fb22d581fcfec4..00000000000000 --- a/src/tooltips.css +++ /dev/null @@ -1,12 +0,0 @@ -@tailwind utilities; - -.footnote { - @apply text-xs !text-[--sl-color-accent] font-semibold p-1 -m-1; -} - -.tippy-box { - background-color: var(--sl-color-bg-nav); - border-color: var(--sl-color-text); - border: 0.1em solid; - color: var(--sl-color-white); -} diff --git a/src/util/description.ts b/src/util/description.ts index c694583bbb3aad..625fcca4661774 100644 --- a/src/util/description.ts +++ b/src/util/description.ts @@ -4,6 +4,7 @@ import { entryToString } from "./container"; import { remark } from "remark"; import strip from "strip-markdown"; import he from "he"; +import { rehypeExternalLinksOptions } from "~/plugins/rehype/external-links"; /** * Generates a plain-text description for use in the `description` and `og:description` meta tags. @@ -33,5 +34,7 @@ export async function getPageDescription( if (paragraph) description = he.decode(paragraph.innerText); } - return description?.replaceAll(" ↗", "").trim(); + return description + ?.replaceAll(rehypeExternalLinksOptions.content.value, "") + .trim(); } diff --git a/src/util/sidebar.ts b/src/util/sidebar.ts index ab2cb411c0610c..5b0a295538af8d 100644 --- a/src/util/sidebar.ts +++ b/src/util/sidebar.ts @@ -1,3 +1,5 @@ +import { readdir } from "fs/promises"; + export function sortBySidebarOrder(a: any, b: any): number { const collator = new Intl.Collator("en"); @@ -6,3 +8,24 @@ export function sortBySidebarOrder(a: any, b: any): number { return collator.compare(a.data.title, b.data.title); } + +async function autogenSections() { + const sections = ( + await readdir("./src/content/docs/", { + withFileTypes: true, + }) + ) + .filter((x) => x.isDirectory()) + .map((x) => x.name); + return sections.map((x) => { + return { + label: x, + autogenerate: { + directory: x, + collapsed: true, + }, + }; + }); +} + +export const sidebar = await autogenSections();