diff --git a/.env b/.env index 5d946ab4..c9bdfa82 100644 --- a/.env +++ b/.env @@ -1,2 +1,7 @@ - MERMAID_DOMAIN="mermaid.live" - MERMAID_ANALYTICS_URL="https://p.mermaid.live" +MERMAID_DOMAIN='' +MERMAID_ANALYTICS_URL='' +MERMAID_RENDERER_URL='https://mermaid.ink' +MERMAID_KROKI_RENDERER='https://kroki.io' +MERMAID_IS_ENABLED_MERMAID_CHART_LINKS='' + +# cp .env .env.local to make local changes diff --git a/.eslintrc.cjs b/.eslintrc.cjs index ed06925b..bc6f10b9 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -86,6 +86,7 @@ module.exports = { j: true, k: true, param: true, + Props: true, req: true, res: true, str: true, diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 87b64abc..45847c44 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -23,6 +23,9 @@ jobs: env: MERMAID_DOMAIN: 'mermaid.live' MERMAID_ANALYTICS_URL: 'https://p.mermaid.live' + MERMAID_RENDERER_URL: 'https://mermaid.ink' + MERMAID_KROKI_RENDERER_URL: 'https://kroki.io' + MERMAID_IS_ENABLED_MERMAID_CHART_LINKS: 'true' run: | export DEPLOY=true [ "$GITHUB_EVENT_NAME" != "pull_request" ] && rm -rf docs/_app/ diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c35dff12..1c1c223e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,9 +17,9 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: yarn-and-build-cache with: path: | @@ -30,21 +30,20 @@ jobs: restore-keys: | ${{ runner.os }}-node_modules-build- - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: - node-version: 18 + node-version-file: '.node-version' cache: 'yarn' # Install NPM dependencies, cache them correctly # and run all Cypress tests - name: Cypress run - uses: cypress-io/github-action@v3 + uses: cypress-io/github-action@v6 with: build: yarn build start: yarn preview wait-on: 'http://localhost:3000' record: true - headless: true parallel: true env: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} diff --git a/.gitignore b/.gitignore index 381928d7..93a49ede 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ yarn-error.log /cypress/downloads /cypress/videos /cypress/screenshots +.env.local diff --git a/.node-version b/.node-version index b8e593f5..5bd68117 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20.15.1 +20.19.0 diff --git a/Dockerfile b/Dockerfile index b400b6a7..8335f14e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ ARG MERMAID_RENDERER_URL ARG MERMAID_KROKI_RENDERER_URL ARG MERMAID_ANALYTICS_URL ARG MERMAID_DOMAIN +ARG MERMAID_IS_ENABLED_MERMAID_CHART_LINKS COPY . ./ diff --git a/README.md b/README.md index 74685558..02a0c7b5 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,18 @@ docker run --platform linux/amd64 --publish 8000:8080 ghcr.io/mermaid-js/mermaid ### To configure renderer URL -When building set the MERMAID_RENDERER_URL build argument to the rendering service. -Default is `https://mermaid.ink` +When building set the MERMAID_RENDERER_URL build argument to the rendering +service. +Example: +Default is`https://mermaid.ink`. +Set to empty string to disable PNG and SVG links under Actions ### To configure Kroki Instance URL -When building set the MERMAID_KROKI_RENDERER_URL build argument to your Kroki instance. +When building set the MERMAID_KROKI_RENDERER_URL build argument to your Kroki +instance. Default is `https://kroki.io` +Set to empty string to disable Kroki link under Actions ### To configure Analytics @@ -43,6 +48,18 @@ When building set the MERMAID_ANALYTICS_URL build argument to your plausible ins Default is empty, disabling analytics. +### To enable Mermaid Chart links and promotion + +When building set the MERMAID_IS_ENABLED_MERMAID_CHART_LINKS build argument to `true` + +Default is empty, disabling button to save to Mermaid Chart and promotional banner. + +### To update the Security modal + +The modal shown on clicking the security link assumes analytics, renderer, Kroki +and Mermaid chart are enabled. You can update it by modifying `Privacy.svelte` +if you wish. + ### Development ```bash diff --git a/cypress/e2e/actions.spec.ts b/cypress/e2e/actions.spec.ts index 24d8d1b1..be9d56ce 100644 --- a/cypress/e2e/actions.spec.ts +++ b/cypress/e2e/actions.spec.ts @@ -37,7 +37,7 @@ describe('Check actions', () => { cy.contains('ER').click(); cy.get(`#downloadPNG`).click(); - verifyFileSizeGreaterThan('diagram', 'png', 40_000); + verifyFileSizeGreaterThan('diagram', 'png', 35_000); cy.get(`#downloadSVG`).click(); verifyFileSizeGreaterThan('diagram', 'svg', 11_000); diff --git a/cypress/e2e/util.ts b/cypress/e2e/util.ts index 2bef6434..6321e65c 100644 --- a/cypress/e2e/util.ts +++ b/cypress/e2e/util.ts @@ -36,7 +36,7 @@ export const verifyFileSizeGreaterThan = ( mode: 'size' }).then((fileSize: number) => { expect(fileSize).to.be.gt(size); - expect(fileSize).to.be.lt(size * 1.3); + expect(fileSize).to.be.lt(size * 2); }); }; diff --git a/cypress/snapshots.js b/cypress/snapshots.js index 350eafa3..7f4afb50 100644 --- a/cypress/snapshots.js +++ b/cypress/snapshots.js @@ -1,7 +1,7 @@ module.exports = { "Site Loads": { "Check Home page load": { - "1": "{\"code\":\"flowchart TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true}" + "1": "{\"code\":\"flowchart TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":true,\"rough\":false,\"updateDiagram\":true,\"panZoom\":false}" }, "Check Redirect from old URL": { "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true}" @@ -19,10 +19,10 @@ module.exports = { "__version": "12.17.4", "Auto sync tests": { "should dim diagram when code is edited": { - "1": "{\"code\":\"flowchart TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":false,\"updateDiagram\":false}" + "1": "{\"code\":\"flowchart TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":false,\"rough\":false,\"updateDiagram\":false,\"panZoom\":false}" }, "should not dim diagram when code is in sync": { - "1": "{\"code\":\"flowchart TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":false}" + "1": "{\"code\":\"flowchart TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"autoSync\":true,\"rough\":false,\"updateDiagram\":false,\"panZoom\":false}" } }, "Test themes": { diff --git a/netlify.toml b/netlify.toml index 4b646fa7..eee8c441 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,3 +1,12 @@ [build.environment] MERMAID_ANALYTICS_URL = 'https://p.mermaid.live' MERMAID_DOMAIN = 'mermaid.live' + MERMAID_RENDERER_URL = 'https://mermaid.ink' + MERMAID_KROKI_RENDERER_URL = 'https://kroki.io' + MERMAID_IS_ENABLED_MERMAID_CHART_LINKS ='true' + +[[redirects]] + from = "/index.html" + to = "/edit" + status = 301 + force = true diff --git a/package.json b/package.json index baf4c593..d3a376b3 100644 --- a/package.json +++ b/package.json @@ -24,63 +24,65 @@ "devDependencies": { "@cypress/snapshot": "2.1.7", "@fortawesome/fontawesome-free": "^6.5.1", - "@sveltejs/adapter-static": "3.0.2", - "@sveltejs/kit": "2.5.19", - "@sveltejs/vite-plugin-svelte": "^3.0.1", - "@testing-library/svelte": "4.2.3", + "@sveltejs/adapter-static": "3.0.8", + "@sveltejs/kit": "2.19.0", + "@sveltejs/vite-plugin-svelte": "^4.0.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/svelte": "^5.2.4", "@types/lodash-es": "^4.17.12", "@types/pako": "2.0.3", "@types/uuid": "9.0.8", "@typescript-eslint/eslint-plugin": "6.21.0", "@typescript-eslint/parser": "6.21.0", - "@vitest/ui": "^1.1.3", + "@vitest/ui": "^2.1.4", "autoprefixer": "^10.4.14", "c8": "7.14.0", "chai": "^4.3.7", "cssnano": "^6.0.0", "cypress": "12.17.4", - "cypress-localstorage-commands": "2.2.6", - "eslint": "8.57.0", + "cypress-localstorage-commands": "2.2.7", + "eslint": "8.57.1", "eslint-config-prettier": "9.1.0", "eslint-plugin-cypress": "2.15.2", "eslint-plugin-es": "^4.1.0", "eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-postcss-modules": "^2.0.0", - "eslint-plugin-svelte": "^2.35.1", + "eslint-plugin-svelte": "^2.45.1", "eslint-plugin-tailwindcss": "^3.13.1", "eslint-plugin-unicorn": "^50.0.1", "eslint-plugin-vitest": "^0.5.0", "esserializer": "^1.3.11", "husky": "^8.0.3", - "jsdom": "^21.1.2", + "jsdom": "^25.0.1", "lint-staged": "^15.2.0", "node-html-parser": "^6.1.5", "postcss": "^8.4.33", "postcss-load-config": "5.1.0", "prettier": "^3.1.0", - "prettier-plugin-svelte": "^3.1.2", + "prettier-plugin-svelte": "^3.2.6", "prettier-plugin-tailwindcss": "^0.6.0", - "svelte": "^4.2.8", - "svelte-preprocess": "^5.1.3", + "svelte": "^5.0.0", + "svelte-preprocess": "^6.0.0", "tailwindcss": "^3.4.1", "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vite": "^5.0.11", - "vitest": "^1.1.3", + "typescript": "^5.5.0", + "vite": "^5.4.4", + "vitest": "^2.1.4", "vitest-dom": "^0.1.1" }, "dependencies": { + "@mermaid-js/layout-elk": "^0.1.4", "@mermaid-js/mermaid-zenuml": "^0.2.0", "daisyui": "2.52.0", "dayjs": "^1.11.7", "js-base64": "3.7.7", "lodash-es": "^4.17.21", - "mermaid": "10.9.1", - "monaco-editor": "0.50.0", + "mermaid": "^11.3.0", + "monaco-editor": "0.52.2", "pako": "2.1.0", "plausible-tracker": "^0.3.8", "random-word-slugs": "0.1.7", - "svg-pan-zoom": "3.6.1", + "svg-pan-zoom": "3.6.2", "svg2roughjs": "^3.2.0", "uuid": "9.0.1" }, @@ -91,7 +93,7 @@ ] }, "volta": { - "node": "18.20.4", + "node": "18.20.7", "yarn": "1.22.22" }, "engines": { diff --git a/src/app.postcss b/src/app.postcss index 528b5111..1f611bac 100644 --- a/src/app.postcss +++ b/src/app.postcss @@ -15,3 +15,7 @@ position: initial; vertical-align: revert; } + +.d { + @apply border border-red-500; +} diff --git a/src/env.d.ts b/src/env.d.ts index ec4a2ff4..d0263111 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -5,6 +5,7 @@ interface ImportMetaEnv { readonly MERMAID_KROKI_RENDERER_URL?: string; readonly MERMAID_ANALYTICS_URL?: string; readonly MERMAID_DOMAIN?: string; + readonly MERMAID_IS_ENABLED_MERMAID_CHART_LINKS?: string; // more env variables... } diff --git a/src/lib/components/Actions.svelte b/src/lib/components/Actions.svelte index 9c0bd1c4..8e703412 100644 --- a/src/lib/components/Actions.svelte +++ b/src/lib/components/Actions.svelte @@ -6,9 +6,9 @@ import { pakoSerde } from '$lib/util/serde'; import { stateStore } from '$lib/util/state'; import { logEvent } from '$lib/util/stats'; + import { version as FAVersion } from '@fortawesome/fontawesome-free/package.json'; import dayjs from 'dayjs'; import { toBase64 } from 'js-base64'; - import { version as FAVersion } from '@fortawesome/fontawesome-free/package.json'; const FONT_AWESOME_URL = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${FAVersion}/css/all.min.css`; @@ -149,7 +149,7 @@ ${svgString}`); logEvent('copyMarkdown'); }; - let gistURL = ''; + let gistURL = $state(''); stateStore.subscribe(({ loader }) => { if (loader?.type === 'gist') { // @ts-expect-error Gist will have url @@ -165,14 +165,14 @@ ${svgString}`); logEvent('loadGist'); }; - let iUrl: string; - let svgUrl: string; - let krokiUrl: string; - let mdCode: string; - let imagemodeselected = 'auto'; - let userimagesize = 1080; + let iUrl: string | undefined = $state(); + let svgUrl: string | undefined = $state(); + let krokiUrl: string | undefined = $state(); + let mdCode: string | undefined = $state(); + let imagemodeselected = $state('auto'); + let userimagesize = $state(1080); - let isNetlify = false; + let isNetlify = $state(false); if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) { isNetlify = true; } @@ -187,31 +187,35 @@ ${svgString}`);
{#if isClipboardAvailable()} - {/if} - - - - - - - - - - - + {#if rendererUrl} + + + + + + + {/if} + {#if krokiRendererUrl} + + + + {/if}
PNG size @@ -238,14 +242,16 @@ ${svgString}`); {/if}
-
- - -
+ {#if rendererUrl} +
+ + +
+ {/if}
{#if isNetlify} diff --git a/src/lib/components/Card/Card.svelte b/src/lib/components/Card/Card.svelte index 3e107487..dbc92dc7 100644 --- a/src/lib/components/Card/Card.svelte +++ b/src/lib/components/Card/Card.svelte @@ -1,14 +1,38 @@
@@ -16,18 +40,18 @@ role="toolbar" tabindex="0" class="bg-primary p-2 {isTabsShown ? 'pb-0' : ''} flex-none cursor-pointer" - on:click={() => (isOpen = !isOpen)} - on:keypress={() => (isOpen = !isOpen)}> + onclick={toggleCardOpen} + onkeypress={toggleCardOpen}>
- +
- + {@render actions?.()}
{#if isOpen}
- + {@render children?.()}
{/if}
diff --git a/src/lib/components/Card/Tabs.svelte b/src/lib/components/Card/Tabs.svelte index 17fa57b3..6796ed8a 100644 --- a/src/lib/components/Card/Tabs.svelte +++ b/src/lib/components/Card/Tabs.svelte @@ -1,34 +1,43 @@
- (isOpen = !isOpen)} - on:keypress|stopPropagation={() => (isOpen = !isOpen)}> - {#if isCloseable} - + + {#if isClosable} + {/if} - {title} + {title} + {#if isOpen && tabs}
    {#each tabs as tab} @@ -36,9 +45,9 @@ role="tab" tabindex="0" class="tab tab-lifted {activeTabID === tab.id ? 'tab-active' : 'text-primary-content'}" - on:click|stopPropagation={() => toggleTabs(tab)} - on:keypress|stopPropagation={() => toggleTabs(tab)}> - + onclick={toggleTabs(tab)} + onkeypress={toggleTabs(tab)}> + {tab.title}
{/each} diff --git a/src/lib/components/Card/card.test.ts b/src/lib/components/Card/card.test.ts index 8fd16961..8c79fc41 100644 --- a/src/lib/components/Card/card.test.ts +++ b/src/lib/components/Card/card.test.ts @@ -1,5 +1,5 @@ import { cleanup, render } from '@testing-library/svelte'; -import { describe, expect, it, afterEach } from 'vitest'; +import { afterEach, describe, expect, it } from 'vitest'; import Card from './Card.svelte'; describe('card.svelte', () => { diff --git a/src/lib/components/DropdownNavMenu.svelte b/src/lib/components/DropdownNavMenu.svelte new file mode 100644 index 00000000..abc1881f --- /dev/null +++ b/src/lib/components/DropdownNavMenu.svelte @@ -0,0 +1,44 @@ + + + diff --git a/src/lib/components/Editor.svelte b/src/lib/components/Editor.svelte index f38604b0..82511fcf 100644 --- a/src/lib/components/Editor.svelte +++ b/src/lib/components/Editor.svelte @@ -1,4 +1,4 @@ -
-
+
{#if $stateStore.error instanceof Error}
-