mirror of
https://github.com/mermaid-js/mermaid-live-editor.git
synced 2025-03-18 17:16:21 +03:00
chore: Cleanup, fix theme
This commit is contained in:
@@ -5,7 +5,7 @@ module.exports = {
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
// 'plugin:@typescript-eslint/strict',
|
||||
'plugin:@typescript-eslint/strict',
|
||||
'prettier'
|
||||
],
|
||||
plugins: ['svelte3', 'tailwindcss', '@typescript-eslint', 'es', 'vitest'],
|
||||
@@ -21,8 +21,17 @@ module.exports = {
|
||||
'tsconfig.json'
|
||||
],
|
||||
overrides: [
|
||||
{ files: ['*.svelte'], processor: 'svelte3/svelte3' }
|
||||
// { files: ['*.ts'], extends: ['plugin:@typescript-eslint/recommended-requiring-type-checking'] }
|
||||
{ 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')
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
'💎 luxury',
|
||||
'🧛♂️ dracula'
|
||||
];
|
||||
|
||||
function checkTheme(theme: string): boolean {
|
||||
return $themeStore.theme !== undefined && theme.includes($themeStore.theme);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="hidden lg:block dropdown">
|
||||
@@ -56,7 +52,7 @@
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<ul tabindex="0" class="p-4 menu compact">
|
||||
{#each themes as theme}
|
||||
<li class:bordered={checkTheme(theme)}>
|
||||
<li class:bordered={$themeStore.theme !== undefined && theme.includes($themeStore.theme)}>
|
||||
<span
|
||||
class="btn btn-ghost justify-start"
|
||||
on:click={() => setTheme(theme)}
|
||||
|
||||
9
src/lib/types.d.ts
vendored
9
src/lib/types.d.ts
vendored
@@ -85,3 +85,12 @@ 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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,15 +42,19 @@ interface GistResponse {
|
||||
}
|
||||
|
||||
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 }: 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]);
|
||||
}
|
||||
@@ -65,7 +69,7 @@ const getGistData = async (gistURL: string): Promise<GistData> => {
|
||||
version: currentItem.version.slice(-7)
|
||||
};
|
||||
} else {
|
||||
throw 'Invalid gist provided';
|
||||
throw new Error('Invalid gist provided');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,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 }: GistResponse = 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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -153,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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -5,7 +5,7 @@ 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 = {
|
||||
@@ -52,7 +52,7 @@ export const currentState: ValidatedState = (() => {
|
||||
};
|
||||
})();
|
||||
|
||||
let q: AsyncQueue<State>;
|
||||
let q: AsyncQueue<State> | undefined;
|
||||
|
||||
const processState = async (state: State) => {
|
||||
const processed: ValidatedState = {
|
||||
@@ -71,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];
|
||||
@@ -111,8 +116,10 @@ export const loadState = (data: string): void => {
|
||||
console.log(`Loading '${data}'`);
|
||||
try {
|
||||
state = deserializeState(data);
|
||||
const mermaidConfig: Record<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' &&
|
||||
@@ -182,7 +189,7 @@ export const updateConfig = (config: string): void => {
|
||||
|
||||
export const toggleDarkTheme = (dark: boolean): void => {
|
||||
inputStateStore.update((state) => {
|
||||
const config: MermaidConfig = 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';
|
||||
}
|
||||
@@ -196,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);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ export const initHandler = async (): Promise<void> => {
|
||||
syncDiagram();
|
||||
initURLSubscription();
|
||||
await initAnalytics();
|
||||
await analytics.page();
|
||||
await analytics?.page();
|
||||
};
|
||||
|
||||
export const isMac = navigator.platform.toUpperCase().includes('MAC');
|
||||
|
||||
Reference in New Issue
Block a user