Skip to content

Commit

Permalink
Merge pull request #1374 from mermaid-js/release-promotion
Browse files Browse the repository at this point in the history
Release live editor
  • Loading branch information
sidharthv96 authored Jan 23, 2024
2 parents d8ba374 + 65d6931 commit d0da4a0
Show file tree
Hide file tree
Showing 18 changed files with 688 additions and 585 deletions.
24 changes: 13 additions & 11 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineConfig } from 'cypress';
import fs from 'fs';
import { isFileExist, findFiles } from 'cy-verify-downloads';
import path from 'path';
export default defineConfig({
projectId: '2ckppp',
viewportWidth: 1440,
Expand All @@ -15,17 +15,19 @@ export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
on('task', {
isFileExist,
findFiles,
deleteFile(path) {
fs.rmSync(path);
return null;
},
readFileMaybe(filename) {
if (fs.existsSync(filename)) {
return fs.readFileSync(filename, 'utf8');
readAndDeleteFile({ fileNamePattern, folder, mode }) {
const fileNameRegex = new RegExp(fileNamePattern);
const files = fs.readdirSync(folder);
const filename = files.find((file) => file.match(fileNameRegex));
const filePath = path.join(folder, filename);
try {
if (mode === 'size') {
return fs.statSync(filePath).size;
}
return fs.readFileSync(filePath, 'utf8');
} finally {
fs.rmSync(filePath);
}
return null;
}
});
},
Expand Down
4 changes: 0 additions & 4 deletions cypress/e2e/actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ describe('Check actions', () => {
});

it('should download png and svg', () => {
cy.clock(new Date(2022, 0, 1).getTime());

cy.get(`#downloadPNG`).click();
verifyFileSizeGreaterThan('diagram', 'png', 34_000);

Expand All @@ -43,7 +41,5 @@ describe('Check actions', () => {

cy.get(`#downloadSVG`).click();
verifyFileSizeGreaterThan('diagram', 'svg', 11_000);

cy.clock().invoke('restore');
});
});
18 changes: 18 additions & 0 deletions cypress/e2e/diagramUpdate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@ describe('Auto sync tests', () => {
cy.getLocalStorage('codeStore').snapshot();
});

it('should automatically defer rendering when complex diagrams are edited', () => {
cy.get('#view').should('not.have.class', 'outOfSync');
typeInEditor(`
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i`);
cy.get('#view').should('have.class', 'outOfSync');
cy.get('#errorContainer').should('contain.text', 'It will be updated automatically.');
// The class should be removed automatically after 1 second.
cy.get('#view').should('not.have.class', 'outOfSync');
});

it('supports commenting code out/in', () => {
cy.get('#editor').contains('Car').click();
cy.get('#editor').get('textarea').type(`${cmd}/`, { force: true });
Expand Down
35 changes: 15 additions & 20 deletions cypress/e2e/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,27 @@ export const verifyFileSizeGreaterThan = (
extension: string,
size: number
) => {
const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
const filePath = `${downloadsFolder}/${fileName}`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
cy.verifyDownload(fileName);
cy.readFile(filePath, null, {
log: false
}).then((buffer: ArrayBuffer) => {
expect(buffer.byteLength).to.be.gt(size);
expect(buffer.byteLength).to.be.lt(size * 1.3);
cy.get('#view').should('not.have.class', 'outOfSync');
cy.task('readAndDeleteFile', {
folder: downloadsFolder,
fileNamePattern: `^mermaid-${fileType}-.*.${extension}$`,
mode: 'size'
}).then((fileSize: number) => {
expect(fileSize).to.be.gt(size);
expect(fileSize).to.be.lt(size * 1.3);
});
cy.task('deleteFile', filePath);
};

export const verifyFileSnapshot = (
fileType: 'history' | 'diagram',
extension: string,
content: string
) => {
const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
const filePath = `${downloadsFolder}/${fileName}`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
cy.verifyDownload(fileName);
cy.readFile(filePath, null, {
log: false
}).then((buffer: ArrayBuffer) =>
expect(new TextDecoder('utf8').decode(buffer)).to.contain(content)
);
cy.task('deleteFile', filePath);
cy.task('readAndDeleteFile', {
folder: downloadsFolder,
fileNamePattern: `^mermaid-${fileType}-.*.${extension}$`,
mode: 'content'
}).then((fileContent: number) => {
expect(fileContent).to.contain(content);
});
};
1 change: 0 additions & 1 deletion cypress/support/e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

// Import commands.js using ES2015 syntax:
import './commands';
require('cy-verify-downloads').addCustomCommand();

// Alternatively you can use CommonJS syntax:
// require('./commands')
Expand Down
2 changes: 1 addition & 1 deletion cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"allowJs": true,
"types": ["cypress", "cypress-localstorage-commands", "cy-verify-downloads", "node"]
"types": ["cypress", "cypress-localstorage-commands", "node"]
},
"include": ["**/*.ts"]
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@
"devDependencies": {
"@cypress/snapshot": "2.1.7",
"@sveltejs/adapter-static": "3.0.1",
"@sveltejs/kit": "2.3.0",
"@sveltejs/kit": "2.4.3",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@testing-library/svelte": "4.0.5",
"@types/lodash-es": "^4.17.12",
"@types/pako": "2.0.3",
"@types/uuid": "9.0.7",
"@typescript-eslint/eslint-plugin": "6.18.1",
"@typescript-eslint/parser": "6.18.1",
"@typescript-eslint/eslint-plugin": "6.19.1",
"@typescript-eslint/parser": "6.19.1",
"@vitest/ui": "^1.1.3",
"autoprefixer": "^10.4.14",
"c8": "7.14.0",
"chai": "^4.3.7",
"cssnano": "^6.0.0",
"cy-verify-downloads": "0.2.2",
"cypress": "12.17.4",
"cypress-localstorage-commands": "2.2.5",
"eslint": "8.56.0",
Expand All @@ -50,7 +50,6 @@
"eslint-plugin-unicorn": "^50.0.1",
"eslint-plugin-vitest": "^0.3.20",
"esserializer": "^1.3.11",
"font-awesome": "^4.7.0",
"husky": "^8.0.3",
"jsdom": "^21.1.2",
"lint-staged": "^15.2.0",
Expand All @@ -72,8 +71,9 @@
"dependencies": {
"daisyui": "2.52.0",
"dayjs": "^1.11.7",
"js-base64": "3.7.5",
"mermaid": "10.6.1",
"js-base64": "3.7.6",
"lodash-es": "^4.17.21",
"mermaid": "10.7.0",
"monaco-editor": "0.45.0",
"pako": "2.1.0",
"plausible-tracker": "^0.3.8",
Expand Down
4 changes: 2 additions & 2 deletions src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
<link rel="manifest" href="%sveltekit.assets%/manifest.json" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"
integrity="sha512-Avb2QiuDEEvB4bZJYdft2mNjVShBftLdPG8FJ0V7irTLQ8Uo0qcPxh4Plq7G5tGm0rU+1SPhVotteLpBERwTkw=="
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA=="
crossorigin="anonymous"
referrerpolicy="no-referrer" />
%sveltekit.head%
Expand Down
17 changes: 11 additions & 6 deletions src/lib/components/Actions.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<script lang="ts">
import { browser } from '$app/environment';
import Card from '$lib/components/Card/Card.svelte';
import { waitForRender } from '$lib/util/autoSync';
import { env } from '$lib/util/env';
import { pakoSerde } from '$lib/util/serde';
import { stateStore } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import { toBase64 } from 'js-base64';
import dayjs from 'dayjs';
import { toBase64 } from 'js-base64';
const { krokiRendererUrl, rendererUrl } = env;
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
Expand All @@ -30,7 +31,11 @@
return toBase64(svgString);
};
const exportImage = (event: Event, exporter: Exporter) => {
const exportImage = async (event: Event, exporter: Exporter) => {
await waitForRender();
if (document.querySelector('.outOfSync')) {
throw new Error('Diagram is out of sync');
}
const canvas: HTMLCanvasElement = document.createElement('canvas');
const svg = document.querySelector<HTMLElement>('#container svg');
if (!svg) {
Expand Down Expand Up @@ -122,13 +127,13 @@
};
};
const onCopyClipboard = (event: Event) => {
exportImage(event, clipboardCopy);
const onCopyClipboard = async (event: Event) => {
await exportImage(event, clipboardCopy);
logEvent('copyClipboard');
};
const onDownloadPNG = (event: Event) => {
exportImage(event, downloadImage);
const onDownloadPNG = async (event: Event) => {
await exportImage(event, downloadImage);
logEvent('download', {
type: 'png'
});
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/Editor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
initEditor(monaco);
errorDebug(100);
errorDebug();
editor = monaco.editor.create(divElement, editorOptions);
editor.onDidChangeModelContent(({ isFlush }) => {
const newText = editor?.getValue();
Expand Down
21 changes: 19 additions & 2 deletions src/lib/components/View.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import { onMount } from 'svelte';
import panzoom from 'svg-pan-zoom';
import type { State, ValidatedState } from '$lib/types';
import { logEvent } from '$lib/util/stats';
import { logEvent, saveStatistics } from '$lib/util/stats';
import { cmdKey } from '$lib/util/util';
import { render as renderDiagram } from '$lib/util/mermaid';
import type { MermaidConfig } from 'mermaid';
import { recordRenderTime, shouldRefreshView } from '$lib/util/autoSync';
let code = '';
let config = '';
Expand Down Expand Up @@ -59,6 +60,7 @@
};
const handleStateChange = async (state: ValidatedState) => {
const startTime = Date.now();
if (state.error !== undefined) {
error = true;
errorLines = state.error.toString().split('\n');
Expand All @@ -76,6 +78,12 @@
if (code === state.code && config === state.mermaid && panZoomEnabled === state.panZoom) {
return;
}
if (!shouldRefreshView()) {
outOfSync = true;
return;
}
code = state.code;
config = state.mermaid;
panZoomEnabled = state.panZoom;
Expand Down Expand Up @@ -114,6 +122,11 @@
console.error('view fail', error_);
error = true;
}
const timeTaken = Date.now() - startTime;
saveStatistics(code, timeTaken);
recordRenderTime(timeTaken, () => {
$inputStateStore.updateDiagram = true;
});
};
onMount(() => {
Expand All @@ -140,7 +153,11 @@
{/each}
{:else}
Diagram out of sync. <br />
Press <i class="fas fa-sync" /> (Sync button) or <kbd>{cmdKey} + Enter</kbd> to sync.
{#if $stateStore.autoSync}
It will be updated automatically.
{:else}
Press <i class="fas fa-sync" /> (Sync button) or <kbd>{cmdKey} + Enter</kbd> to sync.
{/if}
{/if}
</div>
{/if}
Expand Down
49 changes: 49 additions & 0 deletions src/lib/util/autoSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import debounce from 'lodash-es/debounce';
import { get } from 'svelte/store';
import { stateStore } from './state';

let shouldSync = true;
let updater: () => void;
let renderPromise: Promise<void> | undefined;
let resolveRenderPromise: (() => void) | undefined;
const renderDelay = 1000;
const slowRenderThreshold = 150;

const debouncedRender = debounce(() => {
shouldSync = true;
updater();
}, renderDelay);

export const recordRenderTime = (renderTimeMs: number, updaterFunction: () => void): void => {
resolveRenderPromise?.();
const { autoSync } = get(stateStore);
if (!autoSync) {
return;
}
updater = updaterFunction;
const isSlow = renderTimeMs > slowRenderThreshold;
if (!shouldSync) {
debouncedRender();
}
shouldSync = !isSlow;
};

export const shouldRefreshView = (): boolean => {
if (!renderPromise) {
renderPromise = new Promise((resolve) => {
resolveRenderPromise = () => {
renderPromise = undefined;
resolve();
};
});
}

if (!shouldSync) {
debouncedRender();
}
return shouldSync;
};

export const waitForRender = (): Promise<void> => {
return renderPromise ?? Promise.resolve();
};
24 changes: 17 additions & 7 deletions src/lib/util/promos/Jan2024.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
<script lang="ts">
const taglines = [
'Try diagramming with ChatGPT at Mermaid Chart',
"Try Mermaid's Visual Editor at Mermaid Chart",
'Enjoy live collaboration with teammates at Mermaid Chart'
];
const taglines = {
announcement_bar_ai_diagramming: 'Try diagramming with ChatGPT at Mermaid Chart',
announcement_bar_visual_editor: "Try Mermaid's Visual Editor at Mermaid Chart",
announcement_bar_live_collaboration: 'Enjoy live collaboration with teammates at Mermaid Chart'
};
const taglineKeys = Object.keys(taglines);
const taglineKey = taglineKeys[Math.floor(Math.random() * taglineKeys.length)];
const tagline = taglines[taglineKey];
const url =
'https://www.mermaidchart.com/?' +
new URLSearchParams({
utm_source: 'mermaid_live_editor',
utm_medium: taglineKey,
utm_campaign: 'promo_2024'
}).toString();
</script>

<a
href="https://www.mermaidchart.com/"
href={url}
target="_blank"
class="flex flex-grow justify-center gap-6 align-middle tracking-wide">
{taglines[Math.floor(Math.random() * taglines.length)]}
{tagline}
<button class="rounded bg-gray-800 p-1 px-4 text-sm font-light">Try it now</button>
</a>
Loading

0 comments on commit d0da4a0

Please sign in to comment.