Merge pull request #1091 from mermaid-js/develop

Release
This commit is contained in:
Sidharth Vinod
2022-11-15 20:43:45 +05:30
committed by GitHub
37 changed files with 410 additions and 281 deletions
+14 -1
View File
@@ -5,6 +5,7 @@ module.exports = {
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'prettier'
],
plugins: ['svelte3', 'tailwindcss', '@typescript-eslint', 'es', 'vitest'],
@@ -19,7 +20,19 @@ module.exports = {
'package.json',
'tsconfig.json'
],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
overrides: [
{ files: ['*.svelte'], processor: 'svelte3/svelte3' },
{
files: ['*.ts'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'prettier'
]
}
],
settings: {
'svelte3/typescript': () => require('typescript')
},
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
- uses: actions/stale@v6
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
exempt-issue-labels: 'retained'
+1 -1
View File
@@ -1,6 +1,6 @@
{
"editor.formatOnSave": true,
"cSpell.words": ["asyncable", "mindmap", "pako", "Serde", "serdes"],
"cSpell.words": ["asyncable", "KROKI", "mindmap", "pako", "Serde", "serdes", "tailwindcss"],
"vitest.commandLine": "yarn test:unit",
"vitest.enable": true,
"testing.autoRun.mode": "rerun",
+2
View File
@@ -17,6 +17,7 @@ export const verifyFileSize = (
) => {
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
@@ -31,6 +32,7 @@ export const verifyFileSnapshot = (
) => {
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
+9 -9
View File
@@ -29,15 +29,15 @@
"@testing-library/svelte": "3.2.2",
"@types/pako": "2.0.0",
"@types/uuid": "8.3.4",
"@typescript-eslint/eslint-plugin": "5.42.0",
"@typescript-eslint/parser": "5.42.0",
"@vitest/ui": "0.24.3",
"@typescript-eslint/eslint-plugin": "5.43.0",
"@typescript-eslint/parser": "5.43.0",
"@vitest/ui": "0.25.1",
"autoprefixer": "10.4.12",
"c8": "7.12.0",
"chai": "4.3.6",
"cssnano": "5.1.13",
"cy-verify-downloads": "0.1.11",
"cypress": "10.11.0",
"cypress": "11.0.1",
"cypress-localstorage-commands": "2.2.1",
"eslint": "8.27.0",
"eslint-config-prettier": "8.5.0",
@@ -45,7 +45,7 @@
"eslint-plugin-es": "4.1.0",
"eslint-plugin-postcss-modules": "2.0.0",
"eslint-plugin-svelte3": "4.0.0",
"eslint-plugin-tailwindcss": "3.6.2",
"eslint-plugin-tailwindcss": "3.7.0",
"eslint-plugin-vitest": "0.0.11",
"esserializer": "1.3.2",
"husky": "8.0.1",
@@ -56,25 +56,25 @@
"postcss-load-config": "4.0.1",
"prettier": "2.7.1",
"prettier-plugin-svelte": "2.8.0",
"svelte": "3.52.0",
"svelte": "3.53.1",
"svelte-preprocess": "4.10.7",
"tailwindcss": "3.2.1",
"tslib": "2.4.0",
"typescript": "4.8.4",
"vite": "3.2.2",
"vitest": "0.24.3"
"vitest": "0.25.1"
},
"dependencies": {
"@mermaid-js/mermaid-mindmap": "9.2.2",
"analytics": "0.8.1",
"analytics-plugin-plausible": "0.0.6",
"daisyui": "2.38.1",
"daisyui": "2.40.1",
"js-base64": "3.7.2",
"mermaid": "9.2.2",
"moment": "2.29.4",
"monaco-editor": "0.34.1",
"monaco-mermaid": "1.0.6",
"pako": "2.0.4",
"pako": "2.1.0",
"random-word-slugs": "0.1.6",
"svg-pan-zoom": "3.6.1",
"uuid": "9.0.0"
+5 -5
View File
@@ -1,11 +1,11 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly MERMAID_RENDERER_URL: string;
readonly MERMAID_KROKI_RENDERER_URL: string;
readonly MERMAID_CDN_URL: string;
readonly MERMAID_BASE_URL: string;
readonly MERMAID_LOCAL: boolean;
readonly MERMAID_RENDERER_URL?: string;
readonly MERMAID_KROKI_RENDERER_URL?: string;
readonly MERMAID_CDN_URL?: string;
readonly MERMAID_BASE_URL?: string;
readonly MERMAID_LOCAL?: boolean;
// more env variables...
}
+2 -1
View File
@@ -9,7 +9,8 @@ declare global {
namespace jest {
interface Matchers<R = void>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// @ts-expect-error
// eslint-disable-next-line no-undef
extends TestingLibraryMatchers<typeof expect.stringContaining, R> {}
}
}
@@ -1,6 +1,6 @@
<script lang="ts">
import { browser } from '$app/environment';
import Card from '$lib/components/card/card.svelte';
import Card from '$lib/components/Card/Card.svelte';
import { env } from '$lib/util/env';
import { pakoSerde } from '$lib/util/serde';
import { stateStore } from '$lib/util/state';
@@ -15,8 +15,8 @@
`mermaid-diagram-${moment().format('YYYY-MM-DD-HHmmss')}.${ext}`;
const getBase64SVG = (svg?: HTMLElement, width?: number, height?: number): string => {
svg?.setAttribute('height', `${height}px`);
svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
height && svg?.setAttribute('height', `${height}px`);
width && svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
if (!svg) {
svg = getSvgEl();
}
@@ -28,7 +28,10 @@
const exportImage = (event: Event, exporter: Exporter) => {
const canvas: HTMLCanvasElement = document.createElement('canvas');
const svg: HTMLElement = document.querySelector('#container svg');
const svg: HTMLElement | null = document.querySelector('#container svg');
if (!svg) {
throw new Error('svg not found');
}
const box: DOMRect = svg.getBoundingClientRect();
canvas.width = box.width;
canvas.height = box.height;
@@ -43,6 +46,9 @@
}
const context = canvas.getContext('2d');
if (!context) {
throw new Error('context not found');
}
context.fillStyle = 'white';
context.fillRect(0, 0, canvas.width, canvas.height);
@@ -56,12 +62,12 @@
const getSvgEl = () => {
const svgEl: HTMLElement = document
.querySelector('#container svg')
.querySelector('#container svg')!
.cloneNode(true) as HTMLElement;
svgEl.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
const fontAwesomeCdnUrl = Array.from(document.head.getElementsByTagName('link'))
.map((l) => l.href)
.find((h) => h && h.includes('font-awesome'));
.find((h) => h.includes('font-awesome'));
if (fontAwesomeCdnUrl == null) {
return svgEl;
}
@@ -99,10 +105,10 @@
context.drawImage(image, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => {
try {
// @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1004/files
if (!blob) {
throw new Error('blob is empty');
}
void navigator.clipboard.write([
/* eslint-disable no-undef */
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/43821
new ClipboardItem({
[blob.type]: blob
})
@@ -142,7 +148,7 @@
let gistURL = '';
stateStore.subscribe(({ loader }) => {
if (loader?.type === 'gist') {
// @ts-ignore Gist will have url
// @ts-expect-error Gist will have url
gistURL = loader.config.url;
}
});
@@ -166,8 +172,7 @@
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
isNetlify = true;
}
stateStore.subscribe(async (state) => {
const { code, serialized } = await state;
stateStore.subscribe(({ code, serialized }) => {
iUrl = `${rendererUrl}/img/${serialized}?type=png`;
svgUrl = `${rendererUrl}/svg/${serialized}`;
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize(code)}`;
@@ -1,11 +1,11 @@
<script lang="ts">
import type { Tab } from '$lib/types';
import { slide } from 'svelte/transition';
import Tabs from './tabs.svelte';
import Tabs from './Tabs.svelte';
export let isCloseable = true;
export let isOpen = true;
export let tabs: Tab[] = [];
export let activeTabID: string = '';
export let activeTabID = '';
export let title: string;
$: isOpen = isCloseable ? isOpen : true;
$: isTabsShown = isOpen && tabs.length > 0;
@@ -0,0 +1,3 @@
// Vitest Snapshot v1
exports[`card.svelte > mounts 1`] = `"<div><div class=\\"card rounded overflow-hidden m-2 flex-grow flex flex-col shadow-2xl\\"><div class=\\"bg-primary p-2 pb-0 flex-none cursor-pointer\\"><div class=\\"flex justify-between\\"><div class=\\"flex cursor-default s-lTROw8sJayZ1\\"><span class=\\"mr-2 font-semibold s-lTROw8sJayZ1\\"><i class=\\"fas fa-chevron-right icon s-lTROw8sJayZ1 isOpen\\"></i> TabTest</span> <ul class=\\"tabs s-lTROw8sJayZ1\\"><div class=\\"tab tab-lifted tab-active s-lTROw8sJayZ1\\"><i class=\\"mr-1 fab fa-git-alt s-lTROw8sJayZ1\\"></i> title1 </div><div class=\\"tab tab-lifted text-primary-content s-lTROw8sJayZ1\\"><i class=\\"mr-1 far fa-bookmark s-lTROw8sJayZ1\\"></i> title2 </div></ul></div><!--<Tabs>--> <div class=\\"flex gap-x-4 items-center -mt-2\\"></div></div></div> <div class=\\"card-body p-0 flex-grow overflow-auto text-base-content\\"></div></div><!--<Card>--></div>"`;
@@ -1,6 +1,6 @@
import { cleanup, render } from '@testing-library/svelte';
import { describe, expect, it, afterEach } from 'vitest';
import Card from './card.svelte';
import Card from './Card.svelte';
describe('card.svelte', () => {
// TODO: @testing-library/svelte claims to add this automatically but it doesn't work without explicit afterEach
@@ -10,8 +10,8 @@ describe('card.svelte', () => {
const { container } = render(Card, {
title: 'TabTest',
tabs: [
{ id: 't1', title: 'title1' },
{ id: 't2', title: 'title2' }
{ id: 't1', title: 'title1', icon: 'fab fa-git-alt' },
{ id: 't2', title: 'title2', icon: 'far fa-bookmark' }
]
});
expect(container).toBeTruthy();
@@ -8,9 +8,9 @@
import initEditor from 'monaco-mermaid';
import { logEvent } from '$lib/util/stats';
let divEl: HTMLDivElement = null;
let editor: monaco.editor.IStandaloneCodeEditor;
let Monaco: typeof monaco;
let divEl: HTMLDivElement | undefined = undefined;
let editor: monaco.editor.IStandaloneCodeEditor | undefined;
let Monaco: typeof monaco | undefined;
let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
minimap: {
enabled: false
@@ -22,7 +22,9 @@
stateStore.subscribe(({ errorMarkers, editorMode, code, mermaid }) => {
console.log('editor store subscription', { code, mermaid });
if (!editor) return;
if (!editor || !Monaco) {
return;
}
// Update editor text if it's different
const newText = editorMode === 'code' ? code : mermaid;
@@ -34,12 +36,17 @@
// Update editor mode if it's different
const language = editorMode === 'code' ? 'mermaid' : 'json';
if (editor.getModel().getLanguageId() !== language) {
Monaco?.editor.setModelLanguage(editor.getModel(), language);
const model = editor.getModel();
if (!model) {
console.error("editor model doesn't exist");
return;
}
if (model.getLanguageId() !== language) {
Monaco.editor.setModelLanguage(model, language);
}
// Display/clear errors
Monaco?.editor.setModelMarkers(editor.getModel(), 'mermaid', errorMarkers);
Monaco.editor.setModelMarkers(model, 'mermaid', errorMarkers);
});
themeStore.subscribe(({ isDark }) => {
@@ -60,7 +67,7 @@
// errorDebug();
let i = 0;
while (i++ < 500) {
// @ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
// @ts-expect-error : This is a hack to handle a svelte-kit error when importing monaco.
Monaco = window.monaco;
if (Monaco !== undefined) {
return;
@@ -72,13 +79,20 @@
onMount(async () => {
await loadMonaco(); // Fix https://github.com/mermaid-js/mermaid-live-editor/issues/175
if (!Monaco) {
throw new Error('Monaco failed to load');
}
if (!divEl) {
throw new Error('divEl is undefined');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
initEditor(Monaco);
errorDebug(100);
editor = Monaco.editor.create(divEl, editorOptions);
editor.onDidChangeModelContent(({ isFlush, changes }) => {
const newText = editor.getValue();
const newText = editor?.getValue();
console.log('editor onDidChangeModelContent', { text, newText, isFlush, changes });
if (text === newText || isFlush) {
if (!newText || text === newText || isFlush) {
return;
}
text = newText;
@@ -95,19 +109,21 @@
});
}
});
Monaco?.editor.setTheme($themeStore.isDark ? 'mermaid-dark' : 'mermaid');
Monaco.editor.setTheme($themeStore.isDark ? 'mermaid-dark' : 'mermaid');
const resizeObserver = new ResizeObserver((entries) => {
editor.layout({
editor!.layout({
height: entries[0].contentRect.height,
width: entries[0].contentRect.width
});
});
resizeObserver.observe(divEl.parentElement);
if (divEl.parentElement) {
resizeObserver.observe(divEl.parentElement);
}
console.log(`editor mounted`);
return () => {
console.log(`editor disposed`);
editor.dispose();
editor?.dispose();
};
});
</script>
@@ -1,5 +1,5 @@
<script lang="ts">
import Card from '$lib/components/card/card.svelte';
import Card from '$lib/components/Card/Card.svelte';
import { inputStateStore, getStateString } from '$lib/util/state';
import {
addHistoryEntry,
@@ -14,7 +14,7 @@
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import moment from 'moment';
import type { HistoryType, State, Tab } from '$lib/types';
import type { HistoryEntry, HistoryType, State, Tab } from '$lib/types';
import { logEvent } from '$lib/util/stats';
const HISTORY_SAVE_INTERVAL = 60000;
@@ -60,7 +60,7 @@
}
const reader = new FileReader();
reader.onload = (e) => {
const data = JSON.parse(e.target.result as string);
const data: HistoryEntry[] = JSON.parse(e.target.result as string);
restoreHistory(data);
};
reader.readAsText(file);
@@ -7,7 +7,7 @@ import {
historyModeStore,
historyStore
} from './history';
import { defaultState } from '../../util/state';
import { defaultState } from '$lib/util/state';
import { get } from 'svelte/store';
describe('history', () => {
@@ -21,9 +21,9 @@ describe('history', () => {
type: 'manual'
});
const [manualEntry]: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
const [manualEntry] = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualEntry.time).toBe(12345);
expect(manualEntry.type).toBe('manual');
@@ -36,7 +36,9 @@ describe('history', () => {
type: 'auto'
});
const [autoEntry]: HistoryEntry[] = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
const [autoEntry] = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(autoEntry.time).toBe(54321);
expect(autoEntry.type).toBe('auto');
@@ -101,19 +103,23 @@ describe('history migration', () => {
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
);
let manualHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
let autoHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('autoHistoryStore')
);
let manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
let autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
injectHistoryIDs();
manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
});
@@ -1,13 +1,13 @@
<script context="module">
<script context="module" lang="ts">
import { version } from 'mermaid/package.json';
import { analytics } from '$lib/util/stats';
analytics?.track('version', {
void analytics?.track('version', {
mermaidVersion: version
});
</script>
<script lang="ts">
import Theme from './theme.svelte';
import Theme from './Theme.svelte';
interface Link {
title: string;
@@ -1,6 +1,6 @@
<script lang="ts">
import { updateCode } from '$lib/util/state';
import Card from '$lib/components/card/card.svelte';
import Card from '$lib/components/Card/Card.svelte';
import { logEvent } from '$lib/util/stats';
const samples = {
@@ -104,7 +104,8 @@
Mermaid`
};
const loadSampleDiagram = (diagramType: string): void => {
type SampleTypes = keyof typeof samples;
const loadSampleDiagram = (diagramType: SampleTypes): void => {
updateCode(samples[diagramType], {
updateDiagram: true,
resetPanZoom: true
@@ -113,8 +114,8 @@
};
// Adding in this array will add an icon to the preset menu
const newDiagrams: Array<keyof typeof samples> = ['Mindmap'];
const diagramOrder: Array<keyof typeof samples> = [
const newDiagrams: SampleTypes[] = ['Mindmap'];
const diagramOrder: SampleTypes[] = [
'Sequence',
'Flow',
'Class',
@@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { setTheme, themeStore } from '$lib/util/theme';
const themes = [
@@ -52,7 +52,7 @@
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<ul tabindex="0" class="p-4 menu compact">
{#each themes as theme}
<li class={theme.includes($themeStore.theme) ? 'bordered' : ''}>
<li class:bordered={$themeStore.theme !== undefined && theme.includes($themeStore.theme)}>
<span
class="btn btn-ghost justify-start"
on:click={() => setTheme(theme)}
@@ -6,6 +6,7 @@
import { logEvent } from '$lib/util/stats';
import { AsyncQueue, cmdKey } from '$lib/util/util';
import { render as renderDiagram } from '$lib/util/mermaid';
import type { MermaidConfig } from 'mermaid';
let code = '';
let config = '';
@@ -16,9 +17,12 @@
let hide = false;
let manualUpdate = true;
let panZoomEnabled = $stateStore.panZoom;
let pzoom: SvgPanZoom.Instance;
let pzoom: typeof panzoom | undefined;
const handlePanZoomChange = () => {
if (!pzoom) {
return;
}
const pan = pzoom.getPan();
const zoom = pzoom.getZoom();
updateCodeStore({ pan, zoom });
@@ -32,8 +36,11 @@
hide = true;
pzoom?.destroy();
pzoom = undefined;
Promise.resolve().then(() => {
void Promise.resolve().then(() => {
const graphDiv = document.getElementById('graph-div');
if (!graphDiv) {
return;
}
pzoom = panzoom(graphDiv, {
onPan: handlePanZoomChange,
onZoom: handlePanZoomChange,
@@ -70,10 +77,10 @@
code = state.code;
config = state.mermaid;
panZoomEnabled = state.panZoom;
const scroll = view.parentElement.scrollTop;
const scroll = view.parentElement!.scrollTop;
delete container.dataset.processed;
await renderDiagram(
Object.assign({}, JSON.parse(state.mermaid)),
Object.assign({}, JSON.parse(state.mermaid)) as MermaidConfig,
code,
'graph-div',
(svgCode, bindFunctions) => {
@@ -82,6 +89,9 @@
container.innerHTML = svgCode;
// console.log(container.innerHTML);
const graphDiv = document.getElementById('graph-div');
if (!graphDiv) {
throw new Error('graph-div not found');
}
graphDiv.setAttribute('height', '100%');
graphDiv.style.maxWidth = '100%';
if (bindFunctions) {
@@ -90,7 +100,7 @@
}
}
);
view.parentElement.scrollTop = scroll;
view.parentElement!.scrollTop = scroll;
error = false;
} else if (manualUpdate) {
manualUpdate = false;
@@ -106,8 +116,8 @@
const q = new AsyncQueue(handleStateChange);
onMount(() => {
stateStore.subscribe(async (state) => {
await q.process(state);
stateStore.subscribe((state) => {
void q.process(state);
});
window.addEventListener('resize', () => {
if ($stateStore.panZoom && pzoom) {
@@ -1,3 +0,0 @@
// Vitest Snapshot v1
exports[`card.svelte > mounts 1`] = `"<div><div class=\\"card rounded overflow-hidden m-2 flex-grow flex flex-col shadow-2xl\\"><div class=\\"bg-primary p-2 pb-0 flex-none cursor-pointer\\"><div class=\\"flex justify-between\\"><div class=\\"flex cursor-default s-_wx1E_JHsCoF\\"><span class=\\"mr-2 font-semibold s-_wx1E_JHsCoF\\"><i class=\\"fas fa-chevron-right icon s-_wx1E_JHsCoF isOpen\\"></i> TabTest</span> <ul class=\\"tabs s-_wx1E_JHsCoF\\"><div class=\\"tab tab-lifted tab-active s-_wx1E_JHsCoF\\"><i class=\\"mr-1 undefined s-_wx1E_JHsCoF\\"></i> title1 </div><div class=\\"tab tab-lifted text-primary-content s-_wx1E_JHsCoF\\"><i class=\\"mr-1 undefined s-_wx1E_JHsCoF\\"></i> title2 </div></ul></div><!--<Tabs>--> <div class=\\"flex gap-x-4 items-center -mt-2\\"></div></div></div> <div class=\\"card-body p-0 flex-grow overflow-auto text-base-content\\"></div></div><!--<Card>--></div>"`;
+14 -4
View File
@@ -73,14 +73,24 @@ export type HistoryEntry = { id: string; state: State; time: number; url?: strin
}
);
export interface DocConfig {
[key: string]: {
export type DocConfig = Record<
string,
{
code: string;
config?: string;
};
}
}
>;
export type EditorMode = 'code' | 'config';
export type Loader = (url: string) => Promise<State>;
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
export interface ErrorHash {
loc: {
first_line: number;
last_line: number;
first_column: number;
last_column: number;
};
}
+57 -38
View File
@@ -3,17 +3,23 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { State } from '$lib/types';
import { defaultState } from '../state';
import { addHistoryEntry } from '../../components/history/history';
import { defaultState } from '$lib/util/state';
import { addHistoryEntry } from '$lib/components/History/history';
const codeFileName = 'code.mmd';
const configFileName = 'config.json';
interface GithubFile {
truncated: boolean;
raw_url: string;
content: string;
}
const isValidGist = (files: any): boolean => {
return codeFileName in files;
};
const getFileContent = async (file: any): Promise<string> => {
const getFileContent = async (file: GithubFile): Promise<string> => {
if (file.truncated) {
return await (await fetch(file.raw_url)).text();
}
@@ -29,16 +35,26 @@ interface GistData {
url: string;
}
interface GistResponse {
files: Record<string, GithubFile>;
html_url: string;
history: { url: string; committed_at: string; version: string; user: { login: string } }[];
}
const getGistData = async (gistURL: string): Promise<GistData> => {
const path = gistURL.split('github.com').pop();
if (!path) {
throw new Error('Invalid GitHub URL' + gistURL);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
const [_, __, gistID, revisionID] = path.split('/');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { html_url, files, history } = await (
const { html_url, files, history }: GistResponse = await (
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
).json();
if (isValidGist(files)) {
const code = await getFileContent(files[codeFileName]);
let config: string;
let config = '{}';
if (configFileName in files) {
config = await getFileContent(files[configFileName]);
}
@@ -50,10 +66,10 @@ const getGistData = async (gistURL: string): Promise<GistData> => {
config,
author: currentItem.user.login,
time: new Date(currentItem.committed_at).getTime(),
version: (currentItem.version as string).slice(-7)
version: currentItem.version.slice(-7)
};
} else {
throw 'Invalid gist provided';
throw new Error('Invalid gist provided');
}
};
@@ -73,35 +89,38 @@ const getStateFromGist = (gist: GistData, gistURL: string = gist.url): State =>
};
export const loadGistData = async (gistURL: string): Promise<State> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { history } = await (
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
).json();
const gistHistory: GistData[] = [];
for (const entry of history) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const data: GistData = await getGistData(entry.url).catch(() => undefined);
data && gistHistory.push(data);
}
if (gistHistory.length === 0) {
throw 'Invalid gist provided';
}
gistHistory.reverse();
const state = getStateFromGist(gistHistory.slice(-1).pop(), gistURL);
for (const gist of gistHistory) {
addHistoryEntry({
state: getStateFromGist(gist),
time: gist.time,
type: 'loader',
url: gist.url,
name: `${gist.author} v${gist.version}`
});
}
return state;
} catch (err) {
console.error(err);
const path = gistURL.split('github.com').pop();
if (!path) {
throw new Error('Invalid GitHub URL' + gistURL);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, gistID, revisionID] = path.split('/');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { history }: GistResponse = await (
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
).json();
const gistHistory: GistData[] = [];
for (const entry of history) {
const data: GistData | undefined = await getGistData(entry.url).catch(() => undefined);
data && gistHistory.push(data);
}
if (gistHistory.length === 0) {
throw new Error('Invalid gist provided');
}
gistHistory.reverse();
const entry = gistHistory.slice(-1).pop();
if (!entry) {
throw new Error('Invalid gist provided');
}
const state = getStateFromGist(entry, gistURL);
for (const gist of gistHistory) {
addHistoryEntry({
state: getStateFromGist(gist),
time: gist.time,
type: 'loader',
url: gist.url,
name: `${gist.author} v${gist.version}`
});
}
return state;
};
+7 -6
View File
@@ -8,10 +8,11 @@ const loaders: Record<string, Loader> = {
export const loadDataFromUrl = async (): Promise<void> => {
const searchParams = new URLSearchParams(window.location.search);
let state: Partial<State> = defaultState;
let code: string, config: string;
let code: string | undefined = undefined;
let config: string | undefined = undefined;
let loaded = false;
const codeURL: string = searchParams.get('code');
const configURL: string = searchParams.get('config');
const codeURL: string | undefined = searchParams.get('code') ?? undefined;
const configURL: string | undefined = searchParams.get('config') ?? undefined;
if (codeURL) {
code = await (await fetch(codeURL)).text();
@@ -23,9 +24,6 @@ export const loadDataFromUrl = async (): Promise<void> => {
config = defaultState.mermaid;
}
if (!code) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
for (const [key, value] of searchParams.entries()) {
if (key in loaders) {
try {
@@ -38,6 +36,9 @@ export const loadDataFromUrl = async (): Promise<void> => {
}
}
} else {
if (!codeURL) {
throw new Error('Code URL is not defined');
}
state = {
code,
mermaid: config,
+1 -1
View File
@@ -23,5 +23,5 @@ export const render = async (
export const parse = async (code: string): Promise<boolean> => {
await init();
return (await mermaid.parseAsync(code)) as boolean;
return await mermaid.parseAsync(code);
};
+10 -6
View File
@@ -16,18 +16,22 @@ describe('migrations', () => {
it('should migrate from v0 to v1', async () => {
const { applyMigrations } = await import('./migrations');
let manualHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
let autoHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('autoHistoryStore')
);
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
applyMigrations();
manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
});
+2 -2
View File
@@ -1,13 +1,13 @@
import { writable, get, type Writable } from 'svelte/store';
import { persist, localStorage } from '$lib/util/persist';
import { injectHistoryIDs } from '$lib/components/history/history';
import { injectHistoryIDs } from '$lib/components/History/history';
import { logEvent } from './stats';
interface MigrationState {
version: number;
}
const migrations: { [key: string]: () => void } = {
const migrations: Record<string, () => void> = {
injectHistoryIDs
};
+16 -22
View File
@@ -39,16 +39,16 @@ let noWarnings = false;
/**
* List of storages where the warning have already been displayed.
*/
const alreadyWarnFor: Array<string> = [];
const alreadyWarnFor: string[] = [];
/**
* Add a log to indicate that the requested Storage have not been found.
* @param {string} storageName
*/
const warnStorageNotFound = (storageName) => {
const isProduction = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
const warnStorageNotFound = (storageName: string) => {
const isProduction = typeof process !== 'undefined' && process.env.NODE_ENV === 'production';
if (!noWarnings && alreadyWarnFor.indexOf(storageName) === -1 && !isProduction) {
if (!noWarnings && !alreadyWarnFor.includes(storageName) && !isProduction) {
let message = `Unable to find the ${storageName}. No data will be persisted.`;
if (typeof window === 'undefined') {
message +=
@@ -60,17 +60,8 @@ const warnStorageNotFound = (storageName) => {
}
};
const allowedClasses = [];
/**
* Add a class to the allowed list of classes to be serialized
* @param classDef The class to add to the list
*/
export const addSerializableClass = (classDef: () => unknown): void => {
allowedClasses.push(classDef);
};
const serialize = (value: unknown): string => ESSerializer.serialize(value);
const deserialize = (value: string): unknown => {
const deserialize = (value?: string | null): unknown => {
// @TODO: to remove in the next major
if (value === 'undefined') {
return undefined;
@@ -78,7 +69,7 @@ const deserialize = (value: string): unknown => {
if (value !== null && value !== undefined) {
try {
return ESSerializer.deserialize(value, allowedClasses);
return ESSerializer.deserialize(value);
} catch (e) {
// Do nothing
// use the value "as is"
@@ -162,7 +153,7 @@ export function persist<T>(
store.set(initialValue);
}
if ((storage as SelfUpdateStorageInterface<T>).addListener) {
if ('addListener' in storage) {
(storage as SelfUpdateStorageInterface<T>).addListener(key, (newValue) => {
store.set(newValue);
});
@@ -184,7 +175,7 @@ function getBrowserStorage(
browserStorage: Storage,
listenExternalChanges = false
): SelfUpdateStorageInterface<any> {
const listeners: Array<{ key: string; listener: (newValue: any) => void }> = [];
const listeners: { key: string; listener: (newValue: any) => void }[] = [];
const listenerFunction = (event: StorageEvent) => {
const eventKey = event.key;
if (event.storageArea === browserStorage) {
@@ -196,12 +187,14 @@ function getBrowserStorage(
}
};
const connect = () => {
if (listenExternalChanges && typeof window !== 'undefined' && window?.addEventListener) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (listenExternalChanges && typeof window !== 'undefined' && window.addEventListener) {
window.addEventListener('storage', listenerFunction);
}
};
const disconnect = () => {
if (listenExternalChanges && typeof window !== 'undefined' && window?.removeEventListener) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (listenExternalChanges && typeof window !== 'undefined' && window.removeEventListener) {
window.removeEventListener('storage', listenerFunction);
}
};
@@ -237,10 +230,11 @@ function getBrowserStorage(
/**
* Storage implementation that use the browser local storage
* @param {boolean} listenExternalChanges - Update the store if the localStorage is updated from another page
* @param listenExternalChanges - Update the store if the localStorage is updated from another page
*/
export function localStorage<T>(listenExternalChanges = false): StorageInterface<T> {
if (typeof window !== 'undefined' && window?.localStorage) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (typeof window !== 'undefined' && window.localStorage) {
return getBrowserStorage(window.localStorage, listenExternalChanges);
}
warnStorageNotFound('window.localStorage');
@@ -250,7 +244,7 @@ export function localStorage<T>(listenExternalChanges = false): StorageInterface
/**
* Storage implementation that do nothing
*/
export function noopStorage(): StorageInterface<any> {
export function noopStorage<T>(): StorageInterface<T> {
return {
getValue(): null {
return null;
+1 -1
View File
@@ -36,7 +36,7 @@ const serdes: { [key in SerdeType]: Serde } = {
};
export const serializeState = (state: State, serde: SerdeType = 'pako'): string => {
if (serdes[serde] === undefined) {
if (!(serde in serdes)) {
throw new Error(`Unknown serde type: ${serde}`);
}
const json = JSON.stringify(state);
+21 -12
View File
@@ -5,7 +5,9 @@ import { serializeState, deserializeState } from './serde';
import { cmdKey, errorDebug, AsyncQueue } from './util';
import { parse } from './mermaid';
import type { MarkerData, State, ValidatedState } from '$lib/types';
import type { ErrorHash, MarkerData, State, ValidatedState } from '$lib/types';
import type { MermaidConfig } from 'mermaid';
export const defaultState: State = {
code: `graph TD
A[Christmas] -->|Get money| B(Go shopping)
@@ -50,7 +52,7 @@ export const currentState: ValidatedState = (() => {
};
})();
let q: AsyncQueue<State>;
let q: AsyncQueue<State> | undefined;
const processState = async (state: State) => {
const processed: ValidatedState = {
@@ -69,14 +71,19 @@ const processState = async (state: State) => {
processed.error = e;
errorDebug();
console.error(e);
if (e.hash) {
if ('hash' in e) {
const {
loc: { first_line, last_line, first_column, last_column }
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
} = e.hash as ErrorHash;
try {
const marker: MarkerData = {
severity: 8, // Error
startLineNumber: e.hash.loc.first_line,
startColumn: e.hash.loc.first_column,
endLineNumber: e.hash.loc.last_line,
endColumn: (e.hash.loc.last_column as number) + 1,
startLineNumber: first_line,
startColumn: first_column,
endLineNumber: last_line,
endColumn: last_column + 1,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
message: e.str
};
processed.errorMarkers = [marker];
@@ -99,7 +106,7 @@ export const stateStore: Readable<ValidatedState> = derived(
set(newState);
});
}
q.process(state);
void q.process(state);
},
currentState
);
@@ -109,8 +116,10 @@ export const loadState = (data: string): void => {
console.log(`Loading '${data}'`);
try {
state = deserializeState(data);
const mermaidConfig: { [key: string]: string } =
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
const mermaidConfig: MermaidConfig =
typeof state.mermaid === 'string'
? (JSON.parse(state.mermaid) as MermaidConfig)
: state.mermaid;
if (
mermaidConfig.securityLevel &&
mermaidConfig.securityLevel !== 'strict' &&
@@ -180,7 +189,7 @@ export const updateConfig = (config: string): void => {
export const toggleDarkTheme = (dark: boolean): void => {
inputStateStore.update((state) => {
const config = JSON.parse(state.mermaid);
const config = JSON.parse(state.mermaid) as MermaidConfig;
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
config.theme = dark ? 'dark' : 'default';
}
@@ -194,7 +203,7 @@ export const initURLSubscription = (): void => {
stateStore.subscribe(({ serialized }) => {
clearTimeout(urlDebounce);
urlDebounce = window.setTimeout(() => {
history.replaceState(undefined, undefined, `#${serialized}`);
history.replaceState(undefined, '', `#${serialized}`);
}, 250);
});
};
+10 -9
View File
@@ -1,6 +1,6 @@
import { browser } from '$app/environment';
import type { AnalyticsInstance } from 'analytics';
export let analytics: AnalyticsInstance;
export let analytics: AnalyticsInstance | undefined;
export const initAnalytics = async (): Promise<void> => {
if (browser && !analytics) {
@@ -27,7 +27,7 @@ export const initAnalytics = async (): Promise<void> => {
}
};
export const detectType = (text: string): string => {
export const detectType = (text: string): string | undefined => {
const possibleDiagramTypes = [
'classDiagram',
'erDiagram',
@@ -49,7 +49,7 @@ export const detectType = (text: string): string => {
};
export const countLines = (code: string): number => {
return (code.match(/\n/g) || '').length + 1;
return (code.match(/\n/g)?.length ?? 0) + 1;
};
export const saveStatistics = (graph: string): void => {
@@ -80,19 +80,20 @@ const delaysPerEvent = {
themeChange: defaultDelay
};
export type AnalyticsEvent = keyof typeof delaysPerEvent;
const timeouts: Record<string, number> = {};
const timeouts: Map<string, number> = new Map<string, number>();
// manual debounce to reduce the number of events sent to analytics
export const logEvent = (name: AnalyticsEvent, data?: unknown): void => {
if (!analytics) {
return;
}
const key = data ? JSON.stringify({ name, data }) : name;
if (timeouts[key] === undefined) {
if (!timeouts.has(key)) {
void analytics.track(name, data);
} else {
clearTimeout(timeouts[key]);
clearTimeout(timeouts.get(key));
}
timeouts[key] = window.setTimeout(() => {
delete timeouts[key];
}, delaysPerEvent[name]);
timeouts.set(
key,
window.setTimeout(() => timeouts.delete(key), delaysPerEvent[name])
);
};
+2 -2
View File
@@ -23,10 +23,10 @@ export const initHandler = async (): Promise<void> => {
syncDiagram();
initURLSubscription();
await initAnalytics();
analytics?.page();
await analytics?.page();
};
export const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
export const isMac = navigator.platform.toUpperCase().includes('MAC');
export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
let count = 0;
+2 -2
View File
@@ -10,8 +10,8 @@
// 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(() => {
window.addEventListener('hashchange', async (ev) => {
await initHandler();
window.addEventListener('hashchange', () => {
void initHandler();
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker
+11 -7
View File
@@ -1,11 +1,11 @@
<script lang="ts">
import Editor from '$lib/components/editor.svelte';
import Navbar from '$lib/components/navbar.svelte';
import Preset from '$lib/components/preset.svelte';
import Actions from '$lib/components/actions.svelte';
import View from '$lib/components/view.svelte';
import Card from '$lib/components/card/card.svelte';
import History from '$lib/components/history/history.svelte';
import Editor from '$lib/components/Editor.svelte';
import Navbar from '$lib/components/Navbar.svelte';
import Preset from '$lib/components/Preset.svelte';
import Actions from '$lib/components/Actions.svelte';
import View from '$lib/components/View.svelte';
import Card from '$lib/components/Card/Card.svelte';
import History from '$lib/components/History/History.svelte';
import { inputStateStore, stateStore, updateCodeStore } from '$lib/util/state';
import { cmdKey, initHandler, syncDiagram } from '$lib/util/util';
import { onMount } from 'svelte';
@@ -86,6 +86,10 @@
await initHandler();
const resizer = document.getElementById('resizeHandler');
const element = document.getElementById('editorPane');
if (!resizer || !element) {
console.debug('Failed to find resize handler or editor pane', { resizer, element });
return;
}
const resize = (e: { pageX: number }) => {
const newWidth = e.pageX - element.getBoundingClientRect().left;
if (newWidth > 50) {
+1 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import View from '$lib/components/view.svelte';
import View from '$lib/components/View.svelte';
import { initHandler } from '$lib/util/util';
import { onMount } from 'svelte';
onMount(initHandler);
+1
View File
@@ -12,6 +12,7 @@
"compilerOptions": {
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks": true,
"types": ["vitest/importMeta"]
},
"extends": "./.svelte-kit/tsconfig.json"
+116 -84
View File
@@ -483,14 +483,14 @@
dependencies:
"@types/node" "*"
"@typescript-eslint/eslint-plugin@5.42.0":
version "5.42.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.0.tgz#36a8c0c379870127059889a9cc7e05c260d2aaa5"
integrity sha512-5TJh2AgL6+wpL8H/GTSjNb4WrjKoR2rqvFxR/DDTqYNk6uXn8BJMEcncLSpMbf/XV1aS0jAjYwn98uvVCiAywQ==
"@typescript-eslint/eslint-plugin@5.43.0":
version "5.43.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz#4a5248eb31b454715ddfbf8cfbf497529a0a78bc"
integrity sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==
dependencies:
"@typescript-eslint/scope-manager" "5.42.0"
"@typescript-eslint/type-utils" "5.42.0"
"@typescript-eslint/utils" "5.42.0"
"@typescript-eslint/scope-manager" "5.43.0"
"@typescript-eslint/type-utils" "5.43.0"
"@typescript-eslint/utils" "5.43.0"
debug "^4.3.4"
ignore "^5.2.0"
natural-compare-lite "^1.4.0"
@@ -498,14 +498,14 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/parser@5.42.0":
version "5.42.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.42.0.tgz#be0ffbe279e1320e3d15e2ef0ad19262f59e9240"
integrity sha512-Ixh9qrOTDRctFg3yIwrLkgf33AHyEIn6lhyf5cCfwwiGtkWhNpVKlEZApi3inGQR/barWnY7qY8FbGKBO7p3JA==
"@typescript-eslint/parser@5.43.0":
version "5.43.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.43.0.tgz#9c86581234b88f2ba406f0b99a274a91c11630fd"
integrity sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug==
dependencies:
"@typescript-eslint/scope-manager" "5.42.0"
"@typescript-eslint/types" "5.42.0"
"@typescript-eslint/typescript-estree" "5.42.0"
"@typescript-eslint/scope-manager" "5.43.0"
"@typescript-eslint/types" "5.43.0"
"@typescript-eslint/typescript-estree" "5.43.0"
debug "^4.3.4"
"@typescript-eslint/scope-manager@5.36.1":
@@ -516,21 +516,21 @@
"@typescript-eslint/types" "5.36.1"
"@typescript-eslint/visitor-keys" "5.36.1"
"@typescript-eslint/scope-manager@5.42.0":
version "5.42.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.42.0.tgz#e1f2bb26d3b2a508421ee2e3ceea5396b192f5ef"
integrity sha512-l5/3IBHLH0Bv04y+H+zlcLiEMEMjWGaCX6WyHE5Uk2YkSGAMlgdUPsT/ywTSKgu9D1dmmKMYgYZijObfA39Wow==
"@typescript-eslint/scope-manager@5.43.0":
version "5.43.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz#566e46303392014d5d163704724872e1f2dd3c15"
integrity sha512-XNWnGaqAtTJsUiZaoiGIrdJYHsUOd3BZ3Qj5zKp9w6km6HsrjPk/TGZv0qMTWyWj0+1QOqpHQ2gZOLXaGA9Ekw==
dependencies:
"@typescript-eslint/types" "5.42.0"
"@typescript-eslint/visitor-keys" "5.42.0"
"@typescript-eslint/types" "5.43.0"
"@typescript-eslint/visitor-keys" "5.43.0"
"@typescript-eslint/type-utils@5.42.0":
version "5.42.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.42.0.tgz#4206d7192d4fe903ddf99d09b41d4ac31b0b7dca"
integrity sha512-HW14TXC45dFVZxnVW8rnUGnvYyRC0E/vxXShFCthcC9VhVTmjqOmtqj6H5rm9Zxv+ORxKA/1aLGD7vmlLsdlOg==
"@typescript-eslint/type-utils@5.43.0":
version "5.43.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.43.0.tgz#91110fb827df5161209ecca06f70d19a96030be6"
integrity sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==
dependencies:
"@typescript-eslint/typescript-estree" "5.42.0"
"@typescript-eslint/utils" "5.42.0"
"@typescript-eslint/typescript-estree" "5.43.0"
"@typescript-eslint/utils" "5.43.0"
debug "^4.3.4"
tsutils "^3.21.0"
@@ -539,10 +539,10 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.36.1.tgz#1cf0e28aed1cb3ee676917966eb23c2f8334ce2c"
integrity sha512-jd93ShpsIk1KgBTx9E+hCSEuLCUFwi9V/urhjOWnOaksGZFbTOxAT47OH2d4NLJnLhkVD+wDbB48BuaycZPLBg==
"@typescript-eslint/types@5.42.0":
version "5.42.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.42.0.tgz#5aeff9b5eced48f27d5b8139339bf1ef805bad7a"
integrity sha512-t4lzO9ZOAUcHY6bXQYRuu+3SSYdD9TS8ooApZft4WARt4/f2Cj/YpvbTe8A4GuhT4bNW72goDMOy7SW71mZwGw==
"@typescript-eslint/types@5.43.0":
version "5.43.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.43.0.tgz#e4ddd7846fcbc074325293515fa98e844d8d2578"
integrity sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg==
"@typescript-eslint/typescript-estree@5.36.1":
version "5.36.1"
@@ -557,29 +557,29 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@5.42.0":
version "5.42.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.42.0.tgz#2592d24bb5f89bf54a63384ff3494870f95b3fd8"
integrity sha512-2O3vSq794x3kZGtV7i4SCWZWCwjEtkWfVqX4m5fbUBomOsEOyd6OAD1qU2lbvV5S8tgy/luJnOYluNyYVeOTTg==
"@typescript-eslint/typescript-estree@5.43.0":
version "5.43.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz#b6883e58ba236a602c334be116bfc00b58b3b9f2"
integrity sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg==
dependencies:
"@typescript-eslint/types" "5.42.0"
"@typescript-eslint/visitor-keys" "5.42.0"
"@typescript-eslint/types" "5.43.0"
"@typescript-eslint/visitor-keys" "5.43.0"
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.42.0":
version "5.42.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.42.0.tgz#f06bd43b9a9a06ed8f29600273240e84a53f2f15"
integrity sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==
"@typescript-eslint/utils@5.43.0":
version "5.43.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.43.0.tgz#00fdeea07811dbdf68774a6f6eacfee17fcc669f"
integrity sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==
dependencies:
"@types/json-schema" "^7.0.9"
"@types/semver" "^7.3.12"
"@typescript-eslint/scope-manager" "5.42.0"
"@typescript-eslint/types" "5.42.0"
"@typescript-eslint/typescript-estree" "5.42.0"
"@typescript-eslint/scope-manager" "5.43.0"
"@typescript-eslint/types" "5.43.0"
"@typescript-eslint/typescript-estree" "5.43.0"
eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
semver "^7.3.7"
@@ -604,18 +604,18 @@
"@typescript-eslint/types" "5.36.1"
eslint-visitor-keys "^3.3.0"
"@typescript-eslint/visitor-keys@5.42.0":
version "5.42.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.42.0.tgz#ee8d62d486f41cfe646632fab790fbf0c1db5bb0"
integrity sha512-QHbu5Hf/2lOEOwy+IUw0GoSCuAzByTAWWrOTKzTzsotiUnWFpuKnXcAhC9YztAf2EElQ0VvIK+pHJUPkM0q7jg==
"@typescript-eslint/visitor-keys@5.43.0":
version "5.43.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz#cbbdadfdfea385310a20a962afda728ea106befa"
integrity sha512-icl1jNH/d18OVHLfcwdL3bWUKsBeIiKYTGxMJCoGe7xFht+E4QgzOqoWYrU8XSLJWhVw8nTacbm03v23J/hFTg==
dependencies:
"@typescript-eslint/types" "5.42.0"
"@typescript-eslint/types" "5.43.0"
eslint-visitor-keys "^3.3.0"
"@vitest/ui@0.24.3":
version "0.24.3"
resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-0.24.3.tgz#8058e4c9b8a276fbfef1ef0907291f19694a722b"
integrity sha512-f5agYA/sdkiipq42TZayJoeUGjWx+opDjIVeNnb1SG2BNxqObFqh6GuJlzpGHerg4a2+ZSoVo4f4p2+dEyXPmg==
"@vitest/ui@0.25.1":
version "0.25.1"
resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-0.25.1.tgz#09f55e133b2865d0751d0fd75f1bcbcbb60f7d24"
integrity sha512-VjzyfLjNS5Zc7XCCFJW3cM2iVW305D65NG0PIWefA4A8mwOH/QJJ4nFj/4cwXzwL0/VT3/ppvpv3UDNZoh/YOQ==
dependencies:
sirv "^2.0.2"
@@ -661,7 +661,7 @@ acorn-walk@^7.0.0:
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
acorn-walk@^8.0.2:
acorn-walk@^8.0.2, acorn-walk@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
@@ -1459,10 +1459,10 @@ cypress-localstorage-commands@2.2.1:
resolved "https://registry.yarnpkg.com/cypress-localstorage-commands/-/cypress-localstorage-commands-2.2.1.tgz#9895d75a3e8059e78d61231c5794dc46e0fc5fdd"
integrity sha512-m7IwoM+BMOJj7FzzN4It268c/i/Ur72CQX8JwLICkQ6ZgF1XNJOIKlQ7Rszg45ATLTjVTL1QElKCZECoU8cthQ==
cypress@10.11.0:
version "10.11.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-10.11.0.tgz#e9fbdd7638bae3d8fb7619fd75a6330d11ebb4e8"
integrity sha512-lsaE7dprw5DoXM00skni6W5ElVVLGAdRUUdZjX2dYsGjbY/QnpzWZ95Zom1mkGg0hAaO/QVTZoFVS7Jgr/GUPA==
cypress@11.0.1:
version "11.0.1"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-11.0.1.tgz#5332a1825b37ab3f4f81d74389930c55cc7cf31d"
integrity sha512-NuEfd0Vim492RJ3m/+bbTZ3OZrqXgfAfuLaZfIQ9D5lKocS3EDr2tyAarZdAhKwLyoh7OJ33jwMeMFIDbzYqog==
dependencies:
"@cypress/request" "^2.88.10"
"@cypress/xvfb" "^1.2.4"
@@ -2039,10 +2039,10 @@ dagre@^0.8.5:
graphlib "^2.1.8"
lodash "^4.17.15"
daisyui@2.38.1:
version "2.38.1"
resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-2.38.1.tgz#28a3303a1832f41e9804d76ec769335ac1248372"
integrity sha512-fHAVi+bT3H9QCNM7LNEH1EMPT0IwqufuW2RNhTJZorX3TftB1bWW437BVmSt70AZH72Xvp0mVxwVS3ChSKSj0w==
daisyui@2.40.1:
version "2.40.1"
resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-2.40.1.tgz#26e87d3bcc25d657684a60b79099f466eb2431f7"
integrity sha512-nzLpcU8kCLU4zoMQ0hUKw1orRRn2x/LJLZenIsHF+UlGVsml02U4u20nb/knN7B06MPpDZ4RAZyp0rEp67iwzQ==
dependencies:
color "^4.2"
css-selector-tokenizer "^0.8.0"
@@ -2558,14 +2558,14 @@ eslint-plugin-svelte3@4.0.0:
resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-4.0.0.tgz#3d4f3dcaec5761dac8bc697f81de3613b485b4e3"
integrity sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==
eslint-plugin-tailwindcss@3.6.2:
version "3.6.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.6.2.tgz#56ce5722b2972ed45cb4c0bc7062d37b0cfd4995"
integrity sha512-C9Ka3w6W9F5l/3ZDLnkdbqmeujc1vE+p5drjP1Z1UyxgoE1Nh4/f8oQ4XC45PrZsS0Nyv0YKXNH8/dn/FJZKsQ==
eslint-plugin-tailwindcss@3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.7.0.tgz#09f39b18d73141a18e7cb2431bcab89f68010b31"
integrity sha512-e/Pe2jr0vbvNnDXylHoubDGwSh4Z5a4ZhZrkUKHU4MYdSUpjFSofGoMwBSU3d1TybTbo89ENCEWnl1BVAM1iuQ==
dependencies:
fast-glob "^3.2.5"
postcss "^8.4.4"
tailwindcss "^3.1.3"
tailwindcss "^3.2.2"
eslint-plugin-vitest@0.0.11:
version "0.0.11"
@@ -4062,10 +4062,10 @@ p-map@^4.0.0:
dependencies:
aggregate-error "^3.0.0"
pako@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d"
integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==
pako@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
parent-module@^1.0.0:
version "1.0.1"
@@ -5070,10 +5070,10 @@ svelte-preprocess@4.10.7:
sorcery "^0.10.0"
strip-indent "^3.0.0"
svelte@3.52.0:
version "3.52.0"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.52.0.tgz#08259eff20904c63882b66a5d409a55e8c6743b8"
integrity sha512-FxcnEUOAVfr10vDU5dVgJN19IvqeHQCS1zfe8vayTfis9A2t5Fhx+JDe5uv/C3j//bB1umpLJ6quhgs9xyUbCQ==
svelte@3.53.1:
version "3.53.1"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.53.1.tgz#db9d7df7a8f570e8e22547444c149208b1914442"
integrity sha512-Q4/hHkktZogGhN5iqxqSi9sjEVoe/NbIxX4hXEHoasTxj+TxEQVAq66LnDMdAZxjmsodkoI5F3slqsS68U7FNw==
svg-pan-zoom@3.6.1:
version "3.6.1"
@@ -5127,7 +5127,7 @@ tailwindcss@3.2.1:
quick-lru "^5.1.1"
resolve "^1.22.1"
tailwindcss@^3, tailwindcss@^3.1.3:
tailwindcss@^3:
version "3.1.8"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.8.tgz#4f8520550d67a835d32f2f4021580f9fddb7b741"
integrity sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==
@@ -5155,6 +5155,35 @@ tailwindcss@^3, tailwindcss@^3.1.3:
quick-lru "^5.1.1"
resolve "^1.22.1"
tailwindcss@^3.2.2:
version "3.2.4"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.4.tgz#afe3477e7a19f3ceafb48e4b083e292ce0dc0250"
integrity sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==
dependencies:
arg "^5.0.2"
chokidar "^3.5.3"
color-name "^1.1.4"
detective "^5.2.1"
didyoumean "^1.2.2"
dlv "^1.1.3"
fast-glob "^3.2.12"
glob-parent "^6.0.2"
is-glob "^4.0.3"
lilconfig "^2.0.6"
micromatch "^4.0.5"
normalize-path "^3.0.0"
object-hash "^3.0.0"
picocolors "^1.0.0"
postcss "^8.4.18"
postcss-import "^14.1.0"
postcss-js "^4.0.0"
postcss-load-config "^3.1.4"
postcss-nested "6.0.0"
postcss-selector-parser "^6.0.10"
postcss-value-parser "^4.2.0"
quick-lru "^5.1.1"
resolve "^1.22.1"
test-exclude@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@@ -5187,10 +5216,10 @@ tiny-glob@^0.2.9:
globalyzer "0.1.0"
globrex "^0.1.2"
tinybench@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.3.0.tgz#febb2e697c735c0cdb8eb1e43cb1d2fa1821f983"
integrity sha512-zs1gMVBwyyG2QbVchYIbnabRhMOCGvrwZz/q+SV+LIMa9q5YDQZi2kkI6ZRqV2Bz7ba1uvrc7ieUoE4KWnGeKg==
tinybench@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.3.1.tgz#14f64e6b77d7ef0b1f6ab850c7a808c6760b414d"
integrity sha512-hGYWYBMPr7p4g5IarQE7XhlyWveh1EKhy4wUBS1LrHXCKYgvz+4/jCqgmJqZxxldesn05vccrtME2RLLZNW7iA==
tinypool@^0.3.0:
version "0.3.0"
@@ -5432,19 +5461,22 @@ vite@^3.0.0:
optionalDependencies:
fsevents "~2.3.2"
vitest@0.24.3:
version "0.24.3"
resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.24.3.tgz#d91c7e2d557877d5270033efdf18add6063f0c97"
integrity sha512-aM0auuPPgMSstWvr851hB74g/LKaKBzSxcG3da7ejfZbx08Y21JpZmbmDYrMTCGhVZKqTGwzcnLMwyfz2WzkhQ==
vitest@0.25.1:
version "0.25.1"
resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.25.1.tgz#4eafca68fabab22b6099451ab3fcaac33290a4ed"
integrity sha512-eH74h6MkuEgsqR4mAQZeMK9O0PROiKY+i+1GMz/fBi5A3L2ml5U7JQs7LfPU7+uWUziZyLHagl+rkyfR8SLhlA==
dependencies:
"@types/chai" "^4.3.3"
"@types/chai-subset" "^1.3.3"
"@types/node" "*"
acorn "^8.8.0"
acorn-walk "^8.2.0"
chai "^4.3.6"
debug "^4.3.4"
local-pkg "^0.4.2"
source-map "^0.6.1"
strip-literal "^0.4.2"
tinybench "^2.3.0"
tinybench "^2.3.1"
tinypool "^0.3.0"
tinyspy "^1.0.2"
vite "^3.0.0"