Compare commits

...
Author SHA1 Message Date
Sidharth Vinod e953c320fb fix store 2022-08-29 20:49:00 +05:30
Sidharth Vinod a4fe43572b Merge branch 'sidv/historyDownlod' into sidv/updatePersistantStore
* sidv/historyDownlod:
  Metrics
  Tests
  Increase retries
  Temp fix for vite bug
  Update sk
  Update Browserslist
  chore(deps): bump node from 18.7.0 to 18.8.0
  chore(deps-dev): bump @typescript-eslint/eslint-plugin
  chore(deps-dev): bump typescript from 4.7.4 to 4.8.2
  feat: Upload history
  Fix #968 : Ability to download and upload history
  chore(deps): bump daisyui from 2.22.0 to 2.24.0
2022-08-28 23:13:31 +05:30
Sidharth Vinod 04b898ee54 Metrics 2022-08-28 23:10:40 +05:30
Sidharth Vinod 83175127b0 Merge branch 'master' into sidv/historyDownlod
* master:
  Increase retries
  Temp fix for vite bug
  Update sk
  Update Browserslist
  chore(deps): bump node from 18.7.0 to 18.8.0
  chore(deps-dev): bump @typescript-eslint/eslint-plugin
  chore(deps-dev): bump typescript from 4.7.4 to 4.8.2
  Update svelteKit
  chore(deps): bump daisyui from 2.22.0 to 2.24.0
2022-08-28 22:56:26 +05:30
Sidharth Vinod adc9dc7460 Tests 2022-08-28 22:55:47 +05:30
Sidharth VinodandGitHub 9e02aaf962 Merge pull request #990 from mermaid-js/develop
Develop
2022-08-27 13:32:34 +05:30
Sidharth VinodandGitHub 3a48e4e519 Merge pull request #983 from mermaid-js/sidv/upgradeSvelteKit
Upgrade SvelteKit
2022-08-27 13:23:19 +05:30
Sidharth Vinod 6987b6f3c6 Increase retries 2022-08-27 13:07:59 +05:30
Sidharth Vinod d2acfd5d36 Merge branch 'develop' into sidv/upgradeSvelteKit
* develop:
  Update Browserslist
  chore(deps): bump node from 18.7.0 to 18.8.0
  chore(deps-dev): bump @typescript-eslint/eslint-plugin
  chore(deps-dev): bump typescript from 4.7.4 to 4.8.2
  chore(deps): bump daisyui from 2.22.0 to 2.24.0
2022-08-27 12:57:39 +05:30
Sidharth Vinod b384e53d8b Merge branch 'sidv/upgradeSvelteKit' of https://github.com/mermaid-js/mermaid-live-editor into sidv/upgradeSvelteKit
* 'sidv/upgradeSvelteKit' of https://github.com/mermaid-js/mermaid-live-editor:
  Update sk
2022-08-27 12:57:28 +05:30
Sidharth Vinod a6dcabd4b7 Temp fix for vite bug 2022-08-27 12:57:22 +05:30
Sidharth Vinod 082e6dfffc Update sk 2022-08-27 00:22:09 +05:30
sidharthv96 20c23bb338 Update Browserslist 2022-08-26 18:50:24 +00:00
Sidharth VinodandGitHub 6ad4d415b2 Merge pull request #976 from mermaid-js/dependabot/npm_and_yarn/develop/daisyui-2.24.0
chore(deps): bump daisyui from 2.22.0 to 2.24.0
2022-08-27 00:19:03 +05:30
Sidharth VinodandGitHub e2c81de42e Merge pull request #985 from mermaid-js/dependabot/npm_and_yarn/develop/typescript-4.8.2
chore(deps-dev): bump typescript from 4.7.4 to 4.8.2
2022-08-27 00:18:35 +05:30
Sidharth VinodandGitHub e8162c2004 Merge pull request #987 from mermaid-js/dependabot/npm_and_yarn/develop/typescript-eslint/eslint-plugin-5.35.1
chore(deps-dev): bump @typescript-eslint/eslint-plugin from 5.34.0 to 5.35.1
2022-08-27 00:17:57 +05:30
Sidharth VinodandGitHub 5ea615ae26 Merge pull request #988 from mermaid-js/dependabot/docker/develop/node-18.8.0
chore(deps): bump node from 18.7.0 to 18.8.0
2022-08-27 00:17:48 +05:30
dependabot[bot]andGitHub 7c32365935 chore(deps): bump node from 18.7.0 to 18.8.0
Bumps node from 18.7.0 to 18.8.0.

---
updated-dependencies:
- dependency-name: node
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-26 07:21:47 +00:00
dependabot[bot]andGitHub 6c209cddf1 chore(deps-dev): bump @typescript-eslint/eslint-plugin
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.34.0 to 5.35.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.35.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-26 07:02:58 +00:00
dependabot[bot]andGitHub 810315ca10 chore(deps-dev): bump typescript from 4.7.4 to 4.8.2
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.7.4 to 4.8.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.7.4...v4.8.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-26 07:02:19 +00:00
Sidharth Vinod a1312fc095 Merge branch 'master' into sidv/upgradeSvelteKit
* master:
2022-08-25 10:11:58 +05:30
Sidharth VinodandGitHub 9395c1f154 Merge pull request #981 from mermaid-js/develop
Develop
2022-08-25 10:07:29 +05:30
Sidharth Vinod 5a880f3efe feat: Upload history 2022-08-25 10:05:27 +05:30
Sidharth Vinod d160e43eae Fix #968 : Ability to download and upload history 2022-08-25 10:05:19 +05:30
Sidharth Vinod 732e44b6b9 Update lib 2022-08-24 20:25:13 +05:30
dependabot[bot]andGitHub 7ede189894 chore(deps): bump daisyui from 2.22.0 to 2.24.0
Bumps [daisyui](https://github.com/saadeghi/daisyui) from 2.22.0 to 2.24.0.
- [Release notes](https://github.com/saadeghi/daisyui/releases)
- [Changelog](https://github.com/saadeghi/daisyui/blob/master/CHANGELOG.md)
- [Commits](https://github.com/saadeghi/daisyui/commits)

---
updated-dependencies:
- dependency-name: daisyui
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-24 06:48:06 +00:00
Sidharth VinodandGitHub df01b89342 Merge pull request #971 from mermaid-js/develop
Update mermaid
2022-08-23 21:09:59 +05:30
Sidharth VinodandGitHub 69349ba3f4 Merge pull request #961 from mermaid-js/develop
Develop
2022-08-18 12:47:26 +05:30
Sidharth VinodandGitHub 1becc7183d Merge pull request #960 from mermaid-js/develop
Develop
2022-08-18 00:08:08 +05:30
16 changed files with 380 additions and 108 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
# Stop : press ctrl + c
# or
# docker stop mermaid-live-editor
FROM node:18.7.0 as mermaid-live-editor-builder
FROM node:18.8.0 as mermaid-live-editor-builder
COPY --chown=node:node . /home
WORKDIR /home
RUN yarn install
+1 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
defaultCommandTimeout: 16000,
requestTimeout: 16000,
retries: {
runMode: 2,
runMode: 4,
openMode: 0
},
e2e: {
+10 -16
View File
@@ -1,4 +1,4 @@
import { disableDebounce } from './util';
import { disableDebounce, verifyFileSize } from './util';
describe('Check actions', () => {
beforeEach(() => {
cy.clearLocalStorage();
@@ -27,28 +27,22 @@ describe('Check actions', () => {
it('should download png and svg', () => {
cy.clock(new Date(2022, 0, 1).getTime());
const downloadsFolder = Cypress.config('downloadsFolder');
const verifyFileSize = (fileType: string, size: number) => {
cy.get(`#download${fileType.toUpperCase()}`).click();
const fileName = `mermaid-diagram-2022-01-01-000000.${fileType}`;
const filePath = `${downloadsFolder}/${fileName}`;
cy.verifyDownload(fileName);
cy.readFile(filePath, null, {
log: false
}).then((buffer) => expect((buffer as ArrayBuffer).byteLength).to.be.gt(size));
cy.task('deleteFile', filePath);
};
cy.get(`#downloadPNG`).click();
verifyFileSize('diagram', 'png', 21_000);
verifyFileSize('png', 21_000);
verifyFileSize('svg', 11_000);
cy.get(`#downloadSVG`).click();
verifyFileSize('diagram', 'svg', 10_000);
// Verify downloaded file is different for different diagrams
cy.contains('Sample Diagrams').click();
cy.contains('ER Diagram').click();
verifyFileSize('png', 46_000);
verifyFileSize('svg', 12_000);
cy.get(`#downloadPNG`).click();
verifyFileSize('diagram', 'png', 46_000);
cy.get(`#downloadSVG`).click();
verifyFileSize('diagram', 'svg', 12_000);
cy.clock().invoke('restore');
});
+40 -2
View File
@@ -1,8 +1,8 @@
import { getEditor, disableDebounce } from './util';
import { getEditor, disableDebounce, verifyFileSnapshot } from './util';
describe('Save History', () => {
beforeEach(() => {
cy.clock();
cy.clock(new Date(2022, 0, 1).getTime());
cy.clearLocalStorage();
cy.visit('/edit');
disableDebounce();
@@ -10,6 +10,38 @@ describe('Save History', () => {
cy.contains('History').click();
});
afterEach(() => {
cy.clock().invoke('restore');
});
it('should load history from localstorage', () => {
cy.setLocalStorage(
'manualHistoryStore',
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":false,"autoSync":true,"updateDiagram":false},"time":0,"type":"manual","id":"d7ea820e-21dd-418a-b984-fd58acde09df","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":true,"autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"b749ffc6-522b-4a44-86cf-7c1ffc3146b3","name":"helpful-ocean"}]'
);
cy.setLocalStorage(
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":false,"autoSync":true,"updateDiagram":false},"time":0,"type":"auto","id":"69ea820e-522b-4a44-86cf-fd58acde09df","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","updateEditor":true,"autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"x749ffc6-21dd-418a-b984-7c1ffc3146b3","name":"needy-mosquito"}]'
);
cy.reload();
cy.contains('Actions').click();
cy.contains('History').click();
cy.get('#historyList').find('li').should('have.length', 2);
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').contains('helpful-ocean');
cy.get('#historyList').contains('hollow-art');
cy.contains('Restore').click();
cy.contains('Halloween');
cy.contains('Timeline').click();
cy.get('#historyList').find('li').should('have.length', 2);
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').contains('needy-mosquito');
cy.get('#historyList').contains('barking-dog');
cy.contains('Restore').click();
cy.contains('New Year');
});
it('should save when clicked', () => {
cy.get('#historyList').find('li').should('have.length', 0);
cy.get('#historyList').contains('No items in History');
@@ -64,4 +96,10 @@ describe('Save History', () => {
}
cy.get('#historyList').find('li').should('have.length', 30);
});
it('should download history', () => {
cy.get('#saveHistory').click();
cy.get(`#downloadHistory`).click();
verifyFileSnapshot('history', 'json', 'A[Christmas] -->|Get money| B(Go shopping)');
});
});
+32
View File
@@ -9,3 +9,35 @@ export const getEditor = ({ bottom = true, newline = false } = {}) =>
.type(`${newline ? '{enter}' : cmd}`);
export const disableDebounce = () => cy.setLocalStorage('noDebounce', 'true');
const downloadsFolder = Cypress.config('downloadsFolder');
export const verifyFileSize = (
fileType: 'history' | 'diagram',
extension: string,
size: number
) => {
const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
const filePath = `${downloadsFolder}/${fileName}`;
cy.verifyDownload(fileName);
cy.readFile(filePath, null, {
log: false
}).then((buffer) => expect((buffer as ArrayBuffer).byteLength).to.be.gt(size));
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}`;
cy.verifyDownload(fileName);
cy.readFile(filePath, null, {
log: false
}).then((buffer) =>
expect(new TextDecoder('utf-8').decode(buffer as ArrayBuffer)).to.contain(content)
);
cy.task('deleteFile', filePath);
};
+1 -1
View File
@@ -16,7 +16,7 @@ module.exports = {
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}"
}
},
"__version": "10.4.0",
"__version": "10.6.0",
"Auto sync tests": {
"should dim diagram when code is edited": {
"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]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}"
+8 -6
View File
@@ -22,12 +22,13 @@
"devDependencies": {
"@cypress/snapshot": "2.1.7",
"@sveltejs/adapter-static": "1.0.0-next.39",
"@sveltejs/kit": "1.0.0-next.432",
"@sveltejs/kit": "1.0.0-next.442",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/svelte": "3.2.1",
"@types/mermaid": "8.2.9",
"@types/pako": "1.0.3",
"@typescript-eslint/eslint-plugin": "5.34.0",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "5.35.1",
"@typescript-eslint/parser": "5.34.0",
"@vitest/ui": "0.22.1",
"autoprefixer": "10.4.8",
@@ -57,17 +58,17 @@
"svelte-preprocess": "4.10.7",
"tailwindcss": "3.1.8",
"tslib": "2.4.0",
"typescript": "4.7.4",
"typescript": "4.8.2",
"vite": "3.0.9",
"vitest": "0.22.1",
"vitest-svelte-kit": "0.0.7"
},
"dependencies": {
"@analytics/google-analytics": "1.0.3",
"@macfja/svelte-persistent-store": "1.3.0",
"@macfja/svelte-persistent-store": "2.0.0",
"analytics": "0.8.1",
"analytics-plugin-plausible": "^0.0.6",
"daisyui": "2.22.0",
"daisyui": "2.24.0",
"js-base64": "3.7.2",
"mermaid": "9.1.6",
"moment": "2.29.4",
@@ -75,7 +76,8 @@
"monaco-mermaid": "1.0.6",
"pako": "2.0.4",
"random-word-slugs": "0.1.6",
"svg-pan-zoom": "^3.6.1"
"svg-pan-zoom": "^3.6.1",
"uuid": "^8.3.2"
},
"lint-staged": {
"*.{ts,svelte,js,css,md,json}": [
+57 -8
View File
@@ -7,12 +7,15 @@
clearHistoryData,
getPreviousState,
historyStore,
loaderHistoryStore
loaderHistoryStore,
restoreHistory
} from './history';
import { notify, prompt } from '$lib/util/notify';
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import moment from 'moment';
import type { HistoryType, State, Tab } from '$lib/types';
import { logEvent } from '$lib/util/stats';
const HISTORY_SAVE_INTERVAL = 60000;
@@ -32,6 +35,39 @@
}
];
const downloadHistory = () => {
const data = get(historyStore);
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `mermaid-history-${moment().format('YYYY-MM-DD-HHmmss')}.json`;
a.click();
URL.revokeObjectURL(url);
logEvent('history', {
action: 'download'
});
};
const uploadHistory = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'application/json';
input.addEventListener('change', ({ target }: Event) => {
const file = (<HTMLInputElement>target).files[0];
if (!file) {
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const data = JSON.parse(e.target.result as string);
restoreHistory(data);
};
reader.readAsText(file);
});
input.click();
};
const saveHistory = (auto = false) => {
const currentState: string = getStateString();
const previousState: string = getPreviousState(auto);
@@ -46,14 +82,14 @@
}
};
const clearHistory = (date?: number): void => {
if (!date && !prompt('Clear all saved items?')) {
const clearHistory = (id?: string): void => {
if (!id && !prompt('Clear all saved items?')) {
return;
}
clearHistoryData(date);
clearHistoryData(id);
};
const restoreHistory = (state: State): void => {
const restoreHistoryItem = (state: State): void => {
inputStateStore.set({ ...state, updateEditor: true, updateDiagram: true });
};
@@ -88,6 +124,19 @@
<Card on:select={tabSelectHandler} bind:isOpen {tabs} title="History">
<div slot="actions">
<button
id="uploadHistory"
class="btn btn-xs btn-secondary w-12"
on:click|stopPropagation={() => uploadHistory()}
title="Upload history"><i class="fa fa-upload" /></button>
{#if $historyStore.length > 0}
<button
id="downloadHistory"
class="btn btn-xs btn-secondary w-12"
on:click|stopPropagation={() => downloadHistory()}
title="Download history"><i class="fa fa-download" /></button>
{/if}
|
<button
id="saveHistory"
class="btn btn-xs btn-success w-12"
@@ -103,7 +152,7 @@
</div>
<ul class="p-2 space-y-2 overflow-auto h-56" id="historyList">
{#if $historyStore.length > 0}
{#each $historyStore as { state, time, name, url, type }}
{#each $historyStore as { id, state, time, name, url, type }}
<li class="rounded p-2 shadow flex-col">
<div class="flex">
<div class="flex-1">
@@ -121,10 +170,10 @@
</div>
</div>
<div class="flex gap-2 content-center">
<button class="btn btn-success" on:click={() => restoreHistory(state)}
<button class="btn btn-success" on:click={() => restoreHistoryItem(state)}
><i class="fas fa-undo mr-1" />Restore</button>
{#if type !== 'loader'}
<button class="btn btn-error" on:click={() => clearHistory(time)}
<button class="btn btn-error" on:click={() => clearHistory(id)}
><i class="fas fa-trash-alt mr-1" />Delete</button>
{/if}
</div>
+81 -17
View File
@@ -1,26 +1,28 @@
import { derived, writable, get } from 'svelte/store';
import type { Readable, Writable } from 'svelte/store';
import { persist, localStorage } from '@macfja/svelte-persistent-store';
import { persist, createLocalStorage } from '@macfja/svelte-persistent-store';
import { generateSlug } from 'random-word-slugs';
import type { HistoryEntry, HistoryType } from '$lib/types';
import type { HistoryEntry, HistoryType, Optional } from '$lib/types';
import { v4 as uuidV4 } from 'uuid';
import { logEvent } from '$lib/util/stats';
const MAX_AUTO_HISTORY_LENGTH = 30;
export const historyModeStore: Writable<HistoryType> = persist(
writable('manual'),
localStorage(),
createLocalStorage(),
'autoHistoryMode'
);
const autoHistoryStore: Writable<HistoryEntry[]> = persist(
writable([]),
localStorage(),
createLocalStorage(),
'autoHistoryStore'
);
const manualHistoryStore: Writable<HistoryEntry[]> = persist(
writable([]),
localStorage(),
createLocalStorage(),
'manualHistoryStore'
);
@@ -41,28 +43,39 @@ export const historyStore: Readable<HistoryEntry[]> = derived(
}
);
export const addHistoryEntry = (entry: HistoryEntry): void => {
export const addHistoryEntry = (entryToAdd: Optional<HistoryEntry, 'id'>): void => {
const entry: HistoryEntry = {
...entryToAdd,
id: uuidV4()
};
if (entry.type === 'loader') {
loaderHistoryStore.update((entries) => [entry, ...entries]);
return;
}
entry.name = generateSlug(2);
if (entry.type !== 'auto') {
if (!entry.name) {
entry.name = generateSlug(2);
}
if (entry.type === 'auto') {
autoHistoryStore.update((entries) => {
if (entries.length >= MAX_AUTO_HISTORY_LENGTH) {
entries = entries.slice(0, MAX_AUTO_HISTORY_LENGTH - 1);
}
return [entry, ...entries];
});
} else if (entry.type === 'manual') {
manualHistoryStore.update((entries) => [entry, ...entries]);
return;
logEvent('history', { action: 'save' });
}
autoHistoryStore.update((entries) => {
if (entries.length === MAX_AUTO_HISTORY_LENGTH) {
entries.pop();
}
return [entry, ...entries];
});
};
export const clearHistoryData = (time?: number): void => {
export const clearHistoryData = (idToClear?: string): void => {
(get(historyModeStore) === 'auto' ? autoHistoryStore : manualHistoryStore).update((entries) => {
if (get(historyModeStore) !== 'loader') {
entries = entries.filter((entry) => time && entry.time != time);
entries = entries.filter(({ id }) => idToClear && id != idToClear);
logEvent('history', { action: 'clear', type: idToClear ? 'single' : 'all' });
}
return entries;
});
@@ -75,3 +88,54 @@ export const getPreviousState = (auto: boolean): string => {
}
return '';
};
export const restoreHistory = (data: HistoryEntry[]) => {
const entries = data.filter(validateEntry);
const invalidEntryCount = data.length - entries.length;
if (invalidEntryCount > 0) {
console.error(`${invalidEntryCount} invalid history entries were removed.`);
console.error(data);
}
if (entries.length > 0) {
let entryCount = 0;
(entries[0].type === 'auto' ? autoHistoryStore : manualHistoryStore).update((existing) => {
const existingIDs = new Set(existing.map(({ id }) => id));
const newEntries = entries.filter(({ id }) => !existingIDs.has(id));
entryCount = newEntries.length;
const combined = [...existing, ...newEntries];
combined.sort((a, b) => b.time - a.time);
return combined;
});
alert(
`${entryCount} entries restored. ${invalidEntryCount} invalid, ${
entries.length - entryCount
} duplicates.`
);
logEvent('history', {
action: 'restore',
success: entryCount,
invalid: invalidEntryCount,
duplicates: entries.length - entryCount
});
} else {
alert('No valid entries found.');
}
};
export const injectHistoryIDs = (): void => {
const setIDs = (entries: HistoryEntry[]) => {
for (const entry of entries) {
if (!entry.id) {
entry.id = uuidV4();
}
}
return entries;
};
autoHistoryStore.update(setIDs);
manualHistoryStore.update(setIDs);
};
const validateEntry = (entry: HistoryEntry): boolean => {
return entry.type && entry.state && entry.time && true;
};
+11 -7
View File
@@ -68,13 +68,16 @@ export interface LoaderConfig {
config: GistLoaderConfig | FileLoaderConfig;
}
export type HistoryType = 'auto' | 'manual' | 'loader';
export interface HistoryEntry {
state: State;
time: number;
name?: string;
type: HistoryType;
url?: string;
}
export type HistoryEntry = { id: string; state: State; time: number; url?: string } & (
| {
type: 'loader';
name: string;
}
| {
type: HistoryType;
name?: string;
}
);
export interface DocConfig {
[key: string]: {
@@ -84,3 +87,4 @@ export interface DocConfig {
}
export type Loader = (url: string) => Promise<State>;
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
+35
View File
@@ -0,0 +1,35 @@
import { writable, get, type Writable } from 'svelte/store';
import { persist, createLocalStorage } from '@macfja/svelte-persistent-store';
import { injectHistoryIDs } from '$lib/components/history/history';
import { logEvent } from './stats';
interface MigrationState {
version: number;
}
const migrations: { [key: string]: () => void } = {
injectHistoryIDs
};
const migrationStore: Writable<MigrationState> = persist(
writable({ version: -1 }),
createLocalStorage(),
'migrations'
);
export const applyMigrations = (): void => {
const { version }: MigrationState = get(migrationStore);
const allMigrations = Object.entries(migrations);
if (version === allMigrations.length - 1) {
return;
}
console.log(`Current migration version: v${version}. Migrating to v${allMigrations.length - 1}.`);
for (let i = version + 1; i < allMigrations.length; i++) {
const [key, fn] = allMigrations[i];
console.log(`Applying migration ${i}: ${key}.`);
fn();
logEvent('migration', { key });
migrationStore.set({ version: i });
}
logEvent('migration', { status: 'complete', from: version, to: allMigrations.length - 1 });
};
+2 -2
View File
@@ -1,5 +1,5 @@
import { writable, get, derived } from 'svelte/store';
import { persist, localStorage } from '@macfja/svelte-persistent-store';
import { persist, createLocalStorage } from '@macfja/svelte-persistent-store';
import { saveStatistics } from './stats';
import { serializeState, deserializeState } from './serde';
import { cmdKey } from './util';
@@ -40,7 +40,7 @@ const urlParseFailedState = `graph TD
click D href "https://github.com/mermaid-js/mermaid-live-editor/issues/new?assignees=&labels=bug&template=bug_report.md&title=Broken%20link" "Raise issue"`;
// inputStateStore handles all updates and is shared externally when exporting via URL, History, etc.
export const inputStateStore = persist(writable(defaultState), localStorage(), 'codeStore');
export const inputStateStore = persist(writable(defaultState), createLocalStorage(), 'codeStore');
// All internal reads should be done via stateStore, but it should not be persisted/shared externally.
export const stateStore: Readable<ValidatedState> = derived([inputStateStore], ([state]) => {
+2 -2
View File
@@ -1,6 +1,6 @@
import { writable } from 'svelte/store';
import type { Writable } from 'svelte/store';
import { persist, localStorage } from '@macfja/svelte-persistent-store';
import { persist, createLocalStorage } from '@macfja/svelte-persistent-store';
import { logEvent } from './stats';
export interface ThemeConfig {
@@ -12,7 +12,7 @@ export const themeStore: Writable<ThemeConfig> = persist(
writable({
isDark: false
}),
localStorage(),
createLocalStorage(),
'themeStore'
);
+2
View File
@@ -6,10 +6,12 @@
import { setTheme, themeStore } from '$lib/util/theme';
import { toggleDarkTheme } from '$lib/util/state';
import { initHandler } from '$lib/util/util';
import { applyMigrations } from '$lib/util/migrations';
// This can be removed once https://github.com/sveltejs/kit/issues/1612 is fixed.
// Then move it into src and vite will bundle it automatically.
onMount(() => {
applyMigrations();
window.addEventListener('hashchange', async (ev) => {
await initHandler();
});
+8 -1
View File
@@ -1,4 +1,11 @@
import matchers from '@testing-library/jest-dom/matchers';
import { expect } from 'vitest';
import { expect, beforeAll, vi } from 'vitest';
expect.extend(matchers);
// TODO: Remove once https://github.com/sveltejs/kit/issues/6259 is closed.
beforeAll(() => {
vi.mock('$app/env', () => ({
browser: 'window' in globalThis
}));
});
+89 -44
View File
@@ -245,13 +245,19 @@
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@macfja/svelte-persistent-store@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@macfja/svelte-persistent-store/-/svelte-persistent-store-1.3.0.tgz#e3ef6440cde04657ab77284dacd447b12a89cb1e"
integrity sha512-3lPsEAFe28zCNTyA+/pYfUgzRrH6KZvoG0i4IHVhYOs3Xzaqg9hN7LdHa+Qrv7GncXn/6XhDEpb36hB0f7CzXA==
"@macfja/serializer@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@macfja/serializer/-/serializer-1.0.2.tgz#9170d0a298787244d9c9b7d31bbbe64762dd1677"
integrity sha512-ORAF9M5DtarMV6RqZjvVdfsHQ0yTdBDgpA3dFmRBdg/A4TKIwPBjZ9NMjGFQZmJXt/GAHEEMhixu3Rkg5eGWuA==
"@macfja/svelte-persistent-store@2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@macfja/svelte-persistent-store/-/svelte-persistent-store-2.0.0.tgz#2505e1b0a66966355f26e29b1806aec8954ab4c1"
integrity sha512-JX9dre6pFG+lK0GiHefXv9xKEtkSSzHAWsjLj8Kgw/gW5KMzWKaTqvJ0e98Xiv72fzUWWWqgW5Dz5DpHVxbbwA==
dependencies:
"@macfja/serializer" "^1.0.2"
browser-cookies "^1.2.0"
esserializer "^1.3.2"
cyrup "^0.8.1"
idb-keyval "^5.1.3"
"@nodelib/fs.scandir@2.1.5":
@@ -298,10 +304,10 @@
resolved "https://registry.yarnpkg.com/@sveltejs/adapter-static/-/adapter-static-1.0.0-next.39.tgz#ae79b95accd3af6ecfa92e9596c4d41b49cf6fea"
integrity sha512-EeD39H6iEe0UEKnKxLFTZFZpi/FcX5xfbAvsMQ+B09aDZccpQmkJBSIo+4kq1JsQGSjwi/+J3aE9bR67R6CIyQ==
"@sveltejs/kit@1.0.0-next.432":
version "1.0.0-next.432"
resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-1.0.0-next.432.tgz#8abade612d41a52a9364200c4877e2d9a6e28f56"
integrity sha512-weGruEorXX2QJ+OoXbm46snqAaEtJOuSKP5G9VLTa5Ae2pBZE+6N2wWxvgP/iE1r7+9jlrdIyntvxGX0US6GYw==
"@sveltejs/kit@1.0.0-next.442":
version "1.0.0-next.442"
resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-1.0.0-next.442.tgz#9fa8d36067801ee95226c88a8132973fe7bae87d"
integrity sha512-xFGgyQuQEvIrBpdfsGQNG9cHINxpqmaa5LMENUdAt0lpbLuRrc4V2Zi2T11MPT2znNBn3rHEFsqCH9zviEcCRw==
dependencies:
"@sveltejs/vite-plugin-svelte" "^1.0.1"
cookie "^0.5.0"
@@ -458,6 +464,11 @@
dependencies:
"@types/jest" "*"
"@types/uuid@^8.3.4":
version "8.3.4"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
"@types/yauzl@^2.9.1":
version "2.9.1"
resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz"
@@ -465,14 +476,14 @@
dependencies:
"@types/node" "*"
"@typescript-eslint/eslint-plugin@5.34.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.34.0.tgz#d690f60e335596f38b01792e8f4b361d9bd0cb35"
integrity sha512-eRfPPcasO39iwjlUAMtjeueRGuIrW3TQ9WseIDl7i5UWuFbf83yYaU7YPs4j8+4CxUMIsj1k+4kV+E+G+6ypDQ==
"@typescript-eslint/eslint-plugin@5.35.1":
version "5.35.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.35.1.tgz#0d822bfea7469904dfc1bb8f13cabd362b967c93"
integrity sha512-RBZZXZlI4XCY4Wzgy64vB+0slT9+yAPQRjj/HSaRwUot33xbDjF1oN9BLwOLTewoOI0jothIltZRe9uJCHf8gg==
dependencies:
"@typescript-eslint/scope-manager" "5.34.0"
"@typescript-eslint/type-utils" "5.34.0"
"@typescript-eslint/utils" "5.34.0"
"@typescript-eslint/scope-manager" "5.35.1"
"@typescript-eslint/type-utils" "5.35.1"
"@typescript-eslint/utils" "5.35.1"
debug "^4.3.4"
functional-red-black-tree "^1.0.1"
ignore "^5.2.0"
@@ -498,12 +509,20 @@
"@typescript-eslint/types" "5.34.0"
"@typescript-eslint/visitor-keys" "5.34.0"
"@typescript-eslint/type-utils@5.34.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.34.0.tgz#7a324ab9ddd102cd5e1beefc94eea6f3eb32d32d"
integrity sha512-Pxlno9bjsQ7hs1pdWRUv9aJijGYPYsHpwMeCQ/Inavhym3/XaKt1ZKAA8FIw4odTBfowBdZJDMxf2aavyMDkLg==
"@typescript-eslint/scope-manager@5.35.1":
version "5.35.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.35.1.tgz#ccb69d54b7fd0f2d0226a11a75a8f311f525ff9e"
integrity sha512-kCYRSAzIW9ByEIzmzGHE50NGAvAP3wFTaZevgWva7GpquDyFPFcmvVkFJGWJJktg/hLwmys/FZwqM9EKr2u24Q==
dependencies:
"@typescript-eslint/utils" "5.34.0"
"@typescript-eslint/types" "5.35.1"
"@typescript-eslint/visitor-keys" "5.35.1"
"@typescript-eslint/type-utils@5.35.1":
version "5.35.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.35.1.tgz#d50903b56758c5c8fc3be52b3be40569f27f9c4a"
integrity sha512-8xT8ljvo43Mp7BiTn1vxLXkjpw8wS4oAc00hMSB4L1/jIiYbjjnc3Qp2GAUOG/v8zsNCd1qwcqfCQ0BuishHkw==
dependencies:
"@typescript-eslint/utils" "5.35.1"
debug "^4.3.4"
tsutils "^3.21.0"
@@ -512,6 +531,11 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.34.0.tgz#217bf08049e9e7b86694d982e88a2c1566330c78"
integrity sha512-49fm3xbbUPuzBIOcy2CDpYWqy/X7VBkxVN+DC21e0zIm3+61Z0NZi6J9mqPmSW1BDVk9FIOvuCFyUPjXz93sjA==
"@typescript-eslint/types@5.35.1":
version "5.35.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.35.1.tgz#af355fe52a0cc88301e889bc4ada72f279b63d61"
integrity sha512-FDaujtsH07VHzG0gQ6NDkVVhi1+rhq0qEvzHdJAQjysN+LHDCKDKCBRlZFFE0ec0jKxiv0hN63SNfExy0KrbQQ==
"@typescript-eslint/typescript-estree@5.34.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.34.0.tgz#ba7b83f4bf8ccbabf074bbf1baca7a58de3ccb9a"
@@ -525,15 +549,28 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/utils@5.34.0", "@typescript-eslint/utils@^5.17.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.34.0.tgz#0cae98f48d8f9e292e5caa9343611b6faf49e743"
integrity sha512-kWRYybU4Rn++7lm9yu8pbuydRyQsHRoBDIo11k7eqBWTldN4xUdVUMCsHBiE7aoEkFzrUEaZy3iH477vr4xHAQ==
"@typescript-eslint/typescript-estree@5.35.1":
version "5.35.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.35.1.tgz#db878a39a0dbdc9bb133f11cdad451770bfba211"
integrity sha512-JUqE1+VRTGyoXlDWWjm6MdfpBYVq+hixytrv1oyjYIBEOZhBCwtpp5ZSvBt4wIA1MKWlnaC2UXl2XmYGC3BoQA==
dependencies:
"@typescript-eslint/types" "5.35.1"
"@typescript-eslint/visitor-keys" "5.35.1"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/utils@5.35.1", "@typescript-eslint/utils@^5.17.0":
version "5.35.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.35.1.tgz#ae1399afbfd6aa7d0ed1b7d941e9758d950250eb"
integrity sha512-v6F8JNXgeBWI4pzZn36hT2HXXzoBBBJuOYvoQiaQaEEjdi5STzux3Yj8v7ODIpx36i/5s8TdzuQ54TPc5AITQQ==
dependencies:
"@types/json-schema" "^7.0.9"
"@typescript-eslint/scope-manager" "5.34.0"
"@typescript-eslint/types" "5.34.0"
"@typescript-eslint/typescript-estree" "5.34.0"
"@typescript-eslint/scope-manager" "5.35.1"
"@typescript-eslint/types" "5.35.1"
"@typescript-eslint/typescript-estree" "5.35.1"
eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
@@ -545,6 +582,14 @@
"@typescript-eslint/types" "5.34.0"
eslint-visitor-keys "^3.3.0"
"@typescript-eslint/visitor-keys@5.35.1":
version "5.35.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.35.1.tgz#285e9e34aed7c876f16ff646a3984010035898e6"
integrity sha512-cEB1DvBVo1bxbW/S5axbGPE6b7FIMAbo3w+AGq6zNDA7+NYJOIkKj/sInfTv4edxd4PxJSgdN4t6/pbvgA+n5g==
dependencies:
"@typescript-eslint/types" "5.35.1"
eslint-visitor-keys "^3.3.0"
"@vitest/ui@0.22.1":
version "0.22.1"
resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-0.22.1.tgz#549fe2d156bf91824c61cde74bd835c81c4a7996"
@@ -965,9 +1010,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001373:
version "1.0.30001382"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001382.tgz"
integrity sha512-2rtJwDmSZ716Pxm1wCtbPvHtbDWAreTPxXbkc5RkKglow3Ig/4GNGazDI9/BVnXbG/wnv6r3B5FEbkfg9OcTGg==
version "1.0.30001383"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001383.tgz"
integrity sha512-swMpEoTp5vDoGBZsYZX7L7nXHe6dsHxi9o6/LKf/f0LukVtnrxly5GVb/fWdCDTqi/yw6Km6tiJ0pmBacm0gbg==
caseless@~0.12.0:
version "0.12.0"
@@ -1403,6 +1448,11 @@ cypress@10.6.0:
untildify "^4.0.0"
yauzl "^2.10.0"
cyrup@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/cyrup/-/cyrup-0.8.1.tgz#f2be38ea4a6e2f52ebe96731d56c09c2cb8a0f45"
integrity sha512-nBtYqOSyaXLlUbr0KzDiHsvR6jqqrJmbyXBUur2dC1axZjlFm+v++2SYFXKMQC+Dj+0ywy+9WJjV8Eu+6vemYQ==
d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
@@ -1913,10 +1963,10 @@ dagre@^0.8.5:
graphlib "^2.1.8"
lodash "^4.17.15"
daisyui@2.22.0:
version "2.22.0"
resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-2.22.0.tgz#00ea4948fb886ad5242bd6bd3d1a229a8f28d932"
integrity sha512-H08aQP+Mfl2fQOmyaxd1NP54gQbr2xRUj2MmFFvfJ5EDGbthVBICDMNFKyia/zBfR58G8eTJo3CmydglM81/7Q==
daisyui@2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-2.24.0.tgz#7eacfc943815979227cf50b98f7df622c2814118"
integrity sha512-Fdu/4LCdTfWLWAbCuPxvnaRotEfJ+hVPgZ2kv/aUk9RZ00Yk8fGdJtIf0kXJ3IgUKOr8rCXUpfQY6DQU9usPCQ==
dependencies:
color "^4.2"
css-selector-tokenizer "^0.8.0"
@@ -2534,11 +2584,6 @@ esrecurse@^4.3.0:
dependencies:
estraverse "^5.2.0"
esserializer@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/esserializer/-/esserializer-1.3.2.tgz#4788444a7d08108a57ddc6a77602e8c853d3f8da"
integrity sha512-48xQjlQ9Wr6dUfjLAC1IQF0bMIk6m62mN7BH0PklRP0NDfm2ISY16EXM8t2Wfc6WcLKser+X8ggeeFwieBkufQ==
estraverse@^4.1.1:
version "4.3.0"
resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz"
@@ -5061,10 +5106,10 @@ type-fest@^0.21.3:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
typescript@4.7.4:
version "4.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
typescript@4.8.2:
version "4.8.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790"
integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==
undici@^5.8.1:
version "5.10.0"