diff --git a/packages/demo/README.md b/packages/demo/README.md
index f384beb..f8efcfa 100644
--- a/packages/demo/README.md
+++ b/packages/demo/README.md
@@ -5,3 +5,6 @@
- create special code hook, which would produce 2 tabs:
- diagram
- code for the diagram
+ - maybe
+ - https://github.com/withastro/starlight/blob/main/docs/src/components/component-preview.astro
+ - https://starlight.astro.build/components/code/
diff --git a/packages/demo/astro.config.mjs b/packages/demo/astro.config.mjs
index c6c9ca6..a1562d0 100644
--- a/packages/demo/astro.config.mjs
+++ b/packages/demo/astro.config.mjs
@@ -14,7 +14,8 @@ const cache = await getCache();
const className = "not-content";
const conf = {
cache,
- strategy: "f-img-class-dark-mode",
+ strategy: "file",
+ darkScheme: "class",
// do not use .beoe for Netlify deployments
fsPath: "public/beoe",
webPath: "/beoe",
diff --git a/packages/demo/src/content/docs/examples/mermaid-test.md b/packages/demo/src/content/docs/examples/mermaid-test.md
index a94d8b5..0c30ba0 100644
--- a/packages/demo/src/content/docs/examples/mermaid-test.md
+++ b/packages/demo/src/content/docs/examples/mermaid-test.md
@@ -3,17 +3,23 @@ title: mermaid test
draft: true
---
-## rehype-mermaid
-```mermaid
-graph LR
- accTitle: Big Decisions
- accDescr: Bob's Burgers process for making big decisions
- A[Identify Big Decision] --> B{Make Big Decision}
- B --> D[Be done]
-```
+## links
```mermaid strategy=inline class=not-content
+flowchart LR
+ A-->B
+ B-->C
+ C-->D
+ click A callback "Tooltip for a callback"
+ click B "https://www.github.com" "This is a tooltip for a link"
+ click C call callback() "Tooltip for a callback"
+ click D href "https://www.github.com" "This is a tooltip for a link"
+```
+
+## other
+
+```mermaid
graph LR
accTitle: Big Decisions
accDescr: Bob's Burgers process for making big decisions
diff --git a/packages/demo/src/content/docs/index.md b/packages/demo/src/content/docs/index.md
index 7cc75c9..34e92fb 100644
--- a/packages/demo/src/content/docs/index.md
+++ b/packages/demo/src/content/docs/index.md
@@ -2,61 +2,21 @@
title: Diagram All The Things
---
-## Strategies
-
-### inline
-
-- **pros**
- - interactivity
- - links (`a href`)
- - Cmd + F
- - with JS, for example, if we inline JSON data
- - need to be careful with SVGO to not remove IDs etc.
- - dark theme
- - class based
- - `prefers-color-scheme`
- - allows to add dark theme to tools that don't support it out of the box (`graphviz`, `vizdom`, `gnuplot`)
-- **cons**
- - CSS conflicts
- - CSS from website can affect diagram
- - potential solution `.not-content`
- - CSS from diagram can affect website (d2 diagrams)
- - this can break dark theme or other diagrams on the smae page
- - potential solution use unique prefix to make sure there is no clash
- - Dark theme can be limited (black and white)
- - DOM footprint
- - easy to integrate, especially if there is no inline CSS in SVG
-
-### data-url
-
-- **pros**
- - no CSS conflicts
- - dark theme
- - class based. Two images with two classes and only one shown at a time depending on the theme
- - `prefers-color-scheme`: `picture` + `source media="(prefers-color-scheme: dark)` + `img`
- - no DOM footprint
-- **cons**
- - no interactivity
- - no DOM footprint, but overall weight of HTML is high as in previous strategy
- - easy to integrate - only need to add a bit of CSS to support dark theme
-
-### file
-
-- **pros**
- - no CSS conflicts
- - dark theme
- - class based. Two images with two classes and only one shown at a time depending on the theme
- - `prefers-color-scheme`: `picture` + `source media="(prefers-color-scheme: dark)` + `img`
- - no DOM footprint
- - lighter weight of HTML compared to previous strategy
-- **cons**
- - no interactivity
- - some tools don't support dark theme out of the box (`graphviz`, `vizdom`, `gnuplot`)
- - harder to integrate - need to resolve where to write file, how to do cache busting...
-
----
-
-# TODO
+## TODO
+
+- [ ] documentation about shared options
+ - `strategy`
+ - `darkScheme`
+ - `cache`
+ - `class`
+ - `svgo`
+- [ ] documentation about meta
+ - `strategy`, `class`
+ - `alt` (not implemented yet)
+- [ ] documentation and examples about interactivity
+- [ ] tips about styles and other things
+- [ ] revamp site, maybe `template: splash hero: ...` for index page
+- [ ] publish new version
## Strategy
@@ -72,7 +32,7 @@ title: Diagram All The Things
```html
```
@@ -80,7 +40,7 @@ title: Diagram All The Things
```html
```
@@ -105,7 +65,7 @@ title: Diagram All The Things
```
-You would need to add CSS, something like this
+You would need to add CSS, something like this:
```CSS
html[data-theme="light"] .beoe-dark {
@@ -151,3 +111,69 @@ html[data-theme="dark"] .beoe-light {
- Links (``)
- Search text in the diagram (Cmd + F)
- With JS, for example, if we inline JSON data
+
+## Accessibility
+
+````md
+```some alt="..."
+
+```
+````
+
+```html
+
+```
+
+I can add option to [figcaption](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption) as well.
+
+## Old notes
+
+### inline
+
+- **pros**
+ - interactivity
+ - links (`a href`)
+ - Cmd + F
+ - with JS, for example, if we inline JSON data
+ - need to be careful with SVGO to not remove IDs etc.
+ - dark theme
+ - class based
+ - `prefers-color-scheme`
+ - allows to add dark theme to tools that don't support it out of the box (`graphviz`, `vizdom`, `gnuplot`)
+- **cons**
+ - CSS conflicts
+ - CSS from website can affect diagram
+ - potential solution `.not-content`
+ - CSS from diagram can affect website (d2 diagrams)
+ - this can break dark theme or other diagrams on the smae page
+ - potential solution use unique prefix to make sure there is no clash
+ - Dark theme can be limited (black and white)
+ - DOM footprint
+ - easy to integrate, especially if there is no inline CSS in SVG
+
+### data-url
+
+- **pros**
+ - no CSS conflicts
+ - dark theme
+ - class based. Two images with two classes and only one shown at a time depending on the theme
+ - `prefers-color-scheme`: `picture` + `source media="(prefers-color-scheme: dark)` + `img`
+ - no DOM footprint
+- **cons**
+ - no interactivity
+ - no DOM footprint, but overall weight of HTML is high as in previous strategy
+ - easy to integrate - only need to add a bit of CSS to support dark theme
+
+### file
+
+- **pros**
+ - no CSS conflicts
+ - dark theme
+ - class based. Two images with two classes and only one shown at a time depending on the theme
+ - `prefers-color-scheme`: `picture` + `source media="(prefers-color-scheme: dark)` + `img`
+ - no DOM footprint
+ - lighter weight of HTML compared to previous strategy
+- **cons**
+ - no interactivity
+ - some tools don't support dark theme out of the box (`graphviz`, `vizdom`, `gnuplot`)
+ - harder to integrate - need to resolve where to write file, how to do cache busting...
diff --git a/packages/rehype-code-hook-img/README.md b/packages/rehype-code-hook-img/README.md
index 6d3a7a7..acc2ce5 100644
--- a/packages/rehype-code-hook-img/README.md
+++ b/packages/rehype-code-hook-img/README.md
@@ -1,7 +1,3 @@
# @beoe/rehype-code-hook-img
The same as `@beoe/rehype-code-hook`, but specifically designed for hooks that output images (e.g. diagrams).
-
-## TODO
-
-- [ ] Seriously. Strategies need better names
diff --git a/packages/rehype-code-hook-img/src/index.ts b/packages/rehype-code-hook-img/src/index.ts
index 994144b..79ceab9 100644
--- a/packages/rehype-code-hook-img/src/index.ts
+++ b/packages/rehype-code-hook-img/src/index.ts
@@ -38,11 +38,27 @@ export const rehypeCodeHookImg = (
defaults as any as T & BasePluginOptions,
meta
);
- const darkMode =
- opts.strategy === "img-class-dark-mode" ||
- opts.strategy === "picture-dark-mode" ||
- opts.strategy === "f-img-class-dark-mode" ||
- opts.strategy === "f-picture-dark-mode";
+
+ if (opts.strategy === "img") {
+ opts.strategy = "data-url";
+ console.warn("img strategy is deprecated, use data-url instead");
+ }
+ if (opts.strategy === "img-class-dark-mode") {
+ opts.strategy = "data-url";
+ opts.darkScheme = "class";
+ console.warn(
+ "img-class-dark-mode strategy is deprecated, use data-url instead + darkScheme class"
+ );
+ }
+ if (opts.strategy === "picture-dark-mode") {
+ opts.strategy = "data-url";
+ opts.darkScheme = "media";
+ console.warn(
+ "img-class-dark-mode strategy is deprecated, use data-url instead + darkScheme media"
+ );
+ }
+
+ const darkMode = opts.darkScheme != undefined;
const result = render(code, { ...opts, darkMode });
return "then" in result
? result.then((x) => svgStrategy(opts, x))
diff --git a/packages/rehype-code-hook-img/src/types.ts b/packages/rehype-code-hook-img/src/types.ts
index 48116aa..b313c39 100644
--- a/packages/rehype-code-hook-img/src/types.ts
+++ b/packages/rehype-code-hook-img/src/types.ts
@@ -46,16 +46,19 @@ export type BasePluginOptions = {
svgo?: SvgoConfig | boolean;
strategy?: Strategy;
cache?: MapLike;
+ darkScheme?: Scheme;
/**
- * required for file-based strategy
+ * required for `file` strategy
*/
fsPath?: string;
/**
- * required for file-based strategy
+ * required for `file` strategy
*/
webPath?: string;
};
+export type Scheme = "class" | "media";
+
/**
* maybe rename img to data-uri
*/
@@ -64,24 +67,29 @@ export type Strategy =
* SVG directly in the HTML
*/
| "inline"
+ /**
+ * SVG in `src` as [data-url](https://developer.mozilla.org/en-US/docs/Web/URI/Schemes/data)
+ */
+ | "data-url"
+ /**
+ * SVG stored as standalone file
+ */
+ | "file"
/**
* SVG as data-uri in img
+ * @deprecated
*/
| "img"
/**
* SVG as data-uri in img and source inside of a picture
+ * @deprecated
*/
| "picture-dark-mode"
/**
* SVG as data-uri in two imgs with light and dark classes
+ * @deprecated
*/
- | "img-class-dark-mode"
- /**
- * Experiment
- */
- | "f-img"
- | "f-picture-dark-mode"
- | "f-img-class-dark-mode";
+ | "img-class-dark-mode";
export type CbResult = Result | Promise;
diff --git a/packages/rehype-code-hook-img/src/utils.ts b/packages/rehype-code-hook-img/src/utils.ts
index 86b60c6..f72d935 100644
--- a/packages/rehype-code-hook-img/src/utils.ts
+++ b/packages/rehype-code-hook-img/src/utils.ts
@@ -82,7 +82,14 @@ function figure(className: string | undefined, children: any[]) {
}
export function svgStrategy(
- { class: className, strategy, svgo, fsPath, webPath }: BasePluginOptions,
+ {
+ class: className,
+ strategy,
+ svgo,
+ fsPath,
+ webPath,
+ darkScheme,
+ }: BasePluginOptions,
{ svg, data, darkSvg, width, height, alt }: Result
) {
if (svgo !== false) {
@@ -123,98 +130,128 @@ export function svgStrategy(
return url;
};
+ const removeWidthHeight = (svg: string) =>
+ svg
+ .replace(new RegExp(`width="${width}[^"]*"\\s+`), "")
+ .replace(new RegExp(`height="${height}[^"]*"\\s+`), "");
+
+ if (strategy == "file" && (fsPath == undefined || webPath == undefined)) {
+ console.warn(
+ "file strategy requires fsPath and webPath. Falling back to data-url"
+ );
+ strategy = "data-url";
+ }
+
switch (strategy) {
- case "img": {
+ case "data-url": {
+ if (darkScheme == "class" && darkSvg)
+ return figure(
+ className,
+ // wrap in additional div for svg-pan-zoom
+ [
+ h("div", [
+ image({ svg, width, height, alt, class: "beoe-light" }),
+ image({ svg: darkSvg, width, height, alt, class: "beoe-dark" }),
+ ]),
+ ]
+ );
+
+ if (darkScheme == "media" && darkSvg) {
+ const imgLight = image({ svg, width, height, alt });
+ const imgDark = h("source", {
+ width,
+ height,
+ src: svgToMiniDataURI(darkSvg),
+ media: `(prefers-color-scheme: dark)`,
+ });
+
+ return figure(className, [h("picture", [imgLight, imgDark])]);
+ }
+
return getImage();
}
- case "img-class-dark-mode": {
- if (!darkSvg) return getImage();
-
- return figure(
- className,
- // wrap in additional div for svg-pan-zoom
- [
- h("div", [
- image({ svg, width, height, alt, class: "beoe-light" }),
- image({ svg: darkSvg, width, height, alt, class: "beoe-dark" }),
- ]),
- ]
- );
- }
- case "picture-dark-mode": {
- if (!darkSvg) return getImage();
-
- const imgLight = image({ svg, width, height, alt });
- const imgDark = h("source", {
- width,
- height,
- src: svgToMiniDataURI(darkSvg),
- media: `(prefers-color-scheme: dark)`,
- });
+ case "file": {
+ if (fsPath == undefined || webPath == undefined) return;
- return figure(className, [h("picture", [imgLight, imgDark])]);
- }
- case "f-img": {
- if (!fsPath || !webPath) return getImage();
+ if (darkScheme == "class" && darkSvg) {
+ return Promise.all([fileUrl(svg), fileUrl(darkSvg)]).then(
+ ([url, darkUrl]) =>
+ figure(
+ className,
+ // wrap in additional div for svg-pan-zoom
+ [
+ h("div", [
+ image({ width, height, alt, class: "beoe-light", url }),
+ image({
+ width,
+ height,
+ alt,
+ class: "beoe-dark",
+ url: darkUrl,
+ }),
+ ]),
+ ]
+ )
+ );
+ }
+
+ if (darkScheme == "media" && darkSvg)
+ return Promise.all([fileUrl(svg), fileUrl(darkSvg)]).then(
+ ([url, darkUrl]) => {
+ const imgLight = image({ width, height, alt, url });
+ const imgDark = h("source", {
+ width,
+ height,
+ src: darkUrl,
+ media: `(prefers-color-scheme: dark)`,
+ });
+
+ return figure(className, [h("picture", [imgLight, imgDark])]);
+ }
+ );
return fileUrl(svg).then((url) =>
figure(className, [image({ width, height, alt, url })])
);
}
- case "f-img-class-dark-mode": {
- if (!darkSvg || !fsPath || !webPath) return getImage();
-
- return Promise.all([fileUrl(svg), fileUrl(darkSvg)]).then(
- ([url, darkUrl]) =>
- figure(
- className,
- // wrap in additional div for svg-pan-zoom
- [
- h("div", [
- image({ width, height, alt, class: "beoe-light", url }),
- image({
- width,
- height,
- alt,
- class: "beoe-dark",
- url: darkUrl,
- }),
- ]),
- ]
- )
- );
- }
- case "f-picture-dark-mode": {
- if (!darkSvg || !fsPath || !webPath) return getImage();
-
- return Promise.all([fileUrl(svg), fileUrl(darkSvg)]).then(
- ([url, darkUrl]) => {
- const imgLight = image({ width, height, alt, url });
- const imgDark = h("source", {
- width,
- height,
- src: darkUrl,
- media: `(prefers-color-scheme: dark)`,
- });
-
- return figure(className, [h("picture", [imgLight, imgDark])]);
- }
- );
- }
default: {
- svg = svg.replace(new RegExp(`width="${width}[^"]*"\\s+`), "");
- svg = svg.replace(new RegExp(`height="${height}[^"]*"\\s+`), "");
-
- const element = fromHtmlIsomorphic(svg, { fragment: true });
- return {
- type: "element",
- tagName: "figure",
- properties: {
+ if (darkScheme == "class" && darkSvg) {
+ const element = fromHtmlIsomorphic(removeWidthHeight(svg), {
+ fragment: true,
+ });
+ const darkElement = fromHtmlIsomorphic(removeWidthHeight(darkSvg), {
+ fragment: true,
+ });
+ return h(
+ "figure",
+ {
+ class: className,
+ "data-beoe": data ? JSON.stringify(data) : undefined,
+ },
+ [
+ h("div", [
+ h("div", { class: "beoe-light" }, element.children),
+ h("div", { class: "beoe-dark" }, darkElement.children),
+ ]),
+ ]
+ );
+ }
+
+ if (darkScheme == "media") {
+ console.warn("darkScheme media doesn't work for inline strategy");
+ }
+
+ const element = fromHtmlIsomorphic(removeWidthHeight(svg), {
+ fragment: true,
+ });
+ return h(
+ "figure",
+ {
class: className,
"data-beoe": data ? JSON.stringify(data) : undefined,
},
- children: element.children,
- };
+ element.children
+ );
}
}
}