Skip to content

Commit 6f1a4e4

Browse files
Invisible proxy (#157)
* Add mermaid * Add proxying reference * Add concept invisible proxying * Optimised images with calibre/image-actions * Refactor reference in concept and guide * Fix dead link * Improve documentation with new invisible flag * Optimised images with calibre/image-actions --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent d3e6b07 commit 6f1a4e4

14 files changed

+1282
-0
lines changed

.vitepress/components/Mermaid.vue

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<script setup lang="ts">
2+
import { onMounted, onUnmounted, ref } from "vue";
3+
import type { MermaidConfig } from "mermaid";
4+
import mermaid from "mermaid";
5+
6+
const props = defineProps({
7+
graph: {
8+
type: String,
9+
required: true,
10+
},
11+
id: {
12+
type: String,
13+
required: true,
14+
},
15+
});
16+
17+
const svg = ref("");
18+
const code = ref(decodeURIComponent(props.graph));
19+
20+
let mut: MutationObserver | null = null;
21+
22+
onMounted(async () => {
23+
mut = new MutationObserver(() => {
24+
renderChart();
25+
});
26+
mut.observe(document.documentElement, { attributes: true });
27+
28+
await renderChart();
29+
30+
//refresh images on first render
31+
const hasImages = (/<img([\w\W]+?)>/.exec(code.value)?.length ?? 0) > 0;
32+
if (hasImages)
33+
setTimeout(() => {
34+
let imgElements = document.getElementsByTagName("img");
35+
let imgs = Array.from(imgElements);
36+
if (imgs.length) {
37+
Promise.all(
38+
imgs
39+
.filter((img) => !img.complete)
40+
.map(
41+
(img) =>
42+
new Promise((resolve) => {
43+
img.onload = img.onerror = resolve;
44+
}),
45+
),
46+
).then(() => {
47+
renderChart();
48+
});
49+
}
50+
}, 100);
51+
});
52+
53+
onUnmounted(() => mut?.disconnect());
54+
55+
const renderChart = async () => {
56+
const mermaidConfig: MermaidConfig = {
57+
securityLevel: "loose",
58+
startOnLoad: false,
59+
theme: "dark",
60+
};
61+
mermaid.initialize(mermaidConfig);
62+
const render = await mermaid.render(props.id, code.value);
63+
// This is a hack to force v-html to re-render, otherwise the diagram disappears
64+
// when **switching themes** or **reloading the page**.
65+
// The cause is that the diagram is deleted during rendering (out of Vue's knowledge).
66+
// Because svgCode does NOT change, v-html does not re-render.
67+
// This is not required for all diagrams, but it is required for c4c, mindmap and zenuml.
68+
const salt = Math.random().toString(36).substring(7);
69+
svg.value = `${render.svg} <span style="display: none">${salt}</span>`;
70+
};
71+
</script>
72+
73+
<template>
74+
<!-- eslint-disable-next-line vue/no-v-html -->
75+
<div v-html="svg" />
76+
</template>

.vitepress/config.mts

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
referenceSidebar,
88
tutorialsSidebar,
99
} from "./sidebars";
10+
import MermaidExample from "./mermaid";
1011

1112
// https://vitepress.dev/reference/site-config
1213
export default defineConfig({
@@ -34,6 +35,12 @@ export default defineConfig({
3435
],
3536
ignoreDeadLinks: "localhostLinks",
3637

38+
markdown: {
39+
config: (md) => {
40+
MermaidExample(md);
41+
},
42+
},
43+
3744
themeConfig: {
3845
logo: {
3946
src: "/logo.png",

.vitepress/mermaid.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { MarkdownRenderer } from "vitepress";
2+
3+
const MermaidExample = (md: MarkdownRenderer) => {
4+
const defaultRenderer = md.renderer.rules.fence;
5+
6+
if (!defaultRenderer) {
7+
throw new Error("defaultRenderer is undefined");
8+
}
9+
10+
md.renderer.rules.fence = (tokens, index, options, env, slf) => {
11+
const token = tokens[index]!;
12+
const language = token.info.trim();
13+
if (language.startsWith("mermaid")) {
14+
const key = index;
15+
return `
16+
<Suspense>
17+
<template #default>
18+
<Mermaid id="mermaid-${key}" graph="${encodeURIComponent(token.content)}" />
19+
</template>
20+
<!-- loading state via #fallback slot -->
21+
<template #fallback>
22+
Loading...
23+
</template>
24+
</Suspense>
25+
`;
26+
}
27+
28+
return defaultRenderer(tokens, index, options, env, slf);
29+
};
30+
};
31+
32+
export default MermaidExample;

.vitepress/sidebars/concepts.ts

+13
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ export const conceptsSidebar: DefaultTheme.SidebarItem[] = [
3030
},
3131
],
3232
},
33+
{
34+
text: "Proxying",
35+
items: [
36+
{
37+
text: "Traffic splitting",
38+
link: "/concepts/proxying/traffic_splitting",
39+
},
40+
{
41+
text: "Invisible Proxying",
42+
link: "/concepts/proxying/invisible",
43+
},
44+
],
45+
},
3346
{
3447
text: "Workflows",
3548
items: [

.vitepress/sidebars/guides.ts

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ export const guidesSidebar: DefaultTheme.SidebarItem[] = [
5050
text: "Upstream to Another Proxy",
5151
link: "/guides/upstream",
5252
},
53+
{
54+
text: "Enabling invisible proxying",
55+
link: "/guides/invisible_proxying",
56+
},
5357
],
5458
},
5559
{

.vitepress/theme/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import DefaultTheme from "vitepress/theme";
22
import "./custom.css";
33

44
import ProContainer from "../components/Pro.vue";
5+
import Mermaid from "../components/Mermaid.vue";
56
import type { Theme } from "vitepress";
67

78
export default {
89
extends: DefaultTheme,
910
enhanceApp({ app }) {
1011
app.component("ProContainer", ProContainer);
12+
app.component("Mermaid", Mermaid);
1113
},
1214
} satisfies Theme;

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
"eslint-plugin-prettier": "5.2.1",
2020
"eslint-plugin-vue": "9.27.0",
2121
"markdownlint-cli2": "0.14.0",
22+
"mermaid": "11.4.1",
2223
"typescript": "5.6.2",
2324
"typescript-eslint": "8.5.0",
2425
"vitepress": "1.2.2",
26+
"vue": "3.4.27",
2527
"vue-eslint-parser": "9.4.3"
2628
}
2729
}

0 commit comments

Comments
 (0)