Compare commits

...
Author SHA1 Message Date
Sidharth Vinod 76aef33868 Merge branch 'master' of https://github.com/mermaid-js/mermaid-live-editor into gists
* 'master' of https://github.com/mermaid-js/mermaid-live-editor: (50 commits)
  chore: update deps
  chore: update deps
  chore: update deps
  update cache logic
  fix: remove yarn cache
  chore: update deps
  chore: update deps
  chore: update deps
  chore: update deps
  fix: editor undefined issue
  Upgrade of Mermaid to version 8.11.4
  Update mermaid 8.11.3
  Update
  Fix tests
  Update mermaid
  Update deps
  fix: mermaid import
  Typo fix
  Bump autoprefixer from 10.3.0 to 10.3.1
  Bump eslint from 7.29.0 to 7.30.0
  ...
2021-08-15 19:58:09 +05:30
Sidharth Vinod 9801dd606d fix(gist): warnings 2021-06-15 21:26:27 +05:30
Sidharth Vinod b762d1a44a Merge branch 'master' into gists
* master: (28 commits)
  fix: SecurityLevel
  Trigger an alert when securityLevel is stripped
  Only strip securityLevel option when state is loaded from URL
  Fix lint issues
  Update monaco
  Bump tslib from 2.2.0 to 2.3.0
  Bump monaco-editor from 0.24.0 to 0.25.0
  Bump eslint from 7.27.0 to 7.28.0
  Bump cssnano from 5.0.5 to 5.0.6
  Bump postcss from 8.3.0 to 8.3.2
  Bump eslint-plugin-tailwindcss from 1.9.1 to 1.13.2
  Bump cypress from 7.4.0 to 7.5.0
  Handle <img> tag support in graphs
  Force securityLevel option to 'strict' by default to mitigate XSS attacks
  Bump mocha from 8.4.0 to 9.0.0
  Bump @typescript-eslint/eslint-plugin from 4.26.0 to 4.26.1
  Bump prettier from 2.3.0 to 2.3.1
  Bump @typescript-eslint/parser from 4.26.0 to 4.26.1
  Bump normalize-url from 4.5.0 to 4.5.1
  Fix typo in grammer
  ...
2021-06-15 21:20:28 +05:30
Sidharth Vinod 88f9c3b6ea Gist URL 2021-06-05 17:35:44 +05:30
Sidharth Vinod 29ffa1a0a4 Fix base path 2021-06-05 16:31:22 +05:30
Sidharth Vinod 103f02bb90 Fix tests 2021-06-05 16:26:37 +05:30
Sidharth Vinod bf8160e31e Fix gist loading and code not updating issue 2021-06-05 15:55:59 +05:30
Sidharth Vinod 5961c7d451 Fix loading issue 2021-06-05 15:44:26 +05:30
Sidharth Vinod cc3f4fabc2 Loading Gist 2021-06-05 15:31:22 +05:30
Sidharth Vinod aed37c19c7 More tests 2021-06-05 12:18:59 +05:30
Sidharth Vinod 43bac28b30 Format names 2021-06-05 12:13:19 +05:30
Sidharth Vinod ed2057312a Fix test 2021-06-05 11:57:39 +05:30
Sidharth Vinod fdff260f7f Show git revision history 2021-06-05 11:52:13 +05:30
Sidharth Vinod f5878feaa5 Fix file name 2021-06-05 02:53:35 +05:30
Sidharth Vinod aef1afe3f4 Update file names 2021-06-05 02:48:06 +05:30
Sidharth Vinod f2cf3e8111 Fix types 2021-06-05 02:27:42 +05:30
Sidharth Vinod 32351ab24e Load data from urls 2021-06-05 02:25:12 +05:30
Sidharth Vinod bd3b39a2c5 Merge branch 'master' into gists
* master: (338 commits)
  Update build image version
  Update dependencies
  Remove test run on push
  Update dependencies
  Fix docker build issue
  Bump typescript from 4.2.4 to 4.3.2
  Bump autoprefixer from 10.2.5 to 10.2.6
  Enable recording
  Bump tailwindcss from 2.1.2 to 2.1.4
  Bump @macfja/svelte-persistent-store from 1.1.0 to 1.1.1
  Bump @typescript-eslint/eslint-plugin from 4.22.1 to 4.26.0
  Bump eslint-plugin-tailwindcss from 1.8.0 to 1.9.1
  Disable lint
  Fix snapshot
  Fix editor
  Touchup
  Bump prettier from 2.2.1 to 2.3.0
  Test
  Delete docs
  Fix snapshot
  ...
2021-06-05 00:44:55 +05:30
Sidharth Vinod 6708615c48 Load data from gists 2021-03-01 00:25:31 +05:30
17 changed files with 414 additions and 40 deletions
+9 -1
View File
@@ -8,7 +8,15 @@ module.exports = {
'prettier'
],
plugins: ['svelte3', 'tailwindcss', '@typescript-eslint'],
ignorePatterns: ['docs/*', '*.cjs', '*.md', 'snapshots.js', 'svelte.config.js', 'package.json'],
ignorePatterns: [
'docs/*',
'*.cjs',
'*.md',
'snapshots.js',
'svelte.config.js',
'package.json',
'tsconfig.json'
],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
settings: {
'svelte3/typescript': () => require('typescript')
+6
View File
@@ -17,4 +17,10 @@ describe('Check actions', () => {
});
});
});
it('should load gists from URL', () => {
cy.get('#gist').type('https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a');
cy.contains('Load Gist').click();
cy.contains('Go shopping!!');
});
});
+27
View File
@@ -32,6 +32,33 @@ describe('Site Loads', () => {
cy.contains('classDiagram');
});
it('should load diagram from gist', () => {
cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`);
cy.contains('Go shopping!!');
cy.contains('Revisions');
cy.contains('sidharthv96 v8f8f1e2');
cy.contains('sidharthv96 v7851e19');
cy.getLocalStorage('codeStore').snapshot();
});
it('should load diagram from gist revision', () => {
cy.visit(
'/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19'
);
cy.contains('Party');
cy.contains('Revisions');
cy.contains('sidharthv96 v7851e19');
cy.getLocalStorage('codeStore').snapshot();
});
it('should load diagram from raw files', () => {
cy.visit(
'/edit?code=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd&config=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json'
);
cy.contains('Party');
cy.getLocalStorage('codeStore').snapshot();
});
it('should prevent setting the "securityLevel" option via URL', () => {
const b64State = toBase64(
`{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64 />\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
+10 -1
View File
@@ -4,7 +4,16 @@ module.exports = {
"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 \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":true,\"autoSync\":true,\"updateDiagram\":true}"
},
"Check Redirect from old URL": {
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}"
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}"
},
"should load diagram from gist": {
"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\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a\"}}}"
},
"should load diagram from gist revision": {
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19\"}}}"
},
"should load diagram from raw files": {
"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": "8.2.0",
+4
View File
@@ -12,3 +12,7 @@
.action-btn {
@apply rounded p-2 bg-indigo-400 shadow flex-auto text-white hover:bg-indigo-500;
}
.input {
@apply flex-1 border-indigo-400 border-solid border-2 rounded;
}
+33 -4
View File
@@ -105,11 +105,26 @@
);
};
const onCopyMarkdown = (event: Event) => {
event.target.select();
const onCopyMarkdown = () => {
document.getElementById('markdown').select();
document.execCommand('Copy');
};
let gistURL = '';
codeStore.subscribe((state) => {
if (state.loader?.type === 'gist') {
// @ts-ignore Gist will have url
gistURL = state.loader.config.url;
}
});
const loadGist = () => {
if (!gistURL) {
alert('Please enter a Gist URL first');
}
window.location.href = `${window.location.pathname}?gist=${gistURL}`;
};
let iUrl: string;
let svgUrl: string;
let mdCode: string;
@@ -162,8 +177,22 @@
</div>
<div class="w-full flex gap-2 items-center">
<label for="markdown">Copy Markdown</label>
<input class="flex-1" id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} />
<input class="input" id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} />
<label for="markdown">
<button class="btn text-white flex-auto" on:click={onCopyMarkdown}> Copy Markdown </button>
</label>
</div>
<div class="w-full flex gap-2 items-center">
<input
class="input"
id="gist"
type="text"
bind:value={gistURL}
placeholder="Enter Gist URL" />
<label for="gist">
<button class="btn text-white flex-auto" on:click={loadGist}> Load Gist </button>
</label>
</div>
</div>
</Card>
+1 -1
View File
@@ -7,7 +7,7 @@
export let title: string;
export let isOpen = false;
let activeTabID: string = tabs[0]?.id;
$: activeTabID = tabs[0]?.id;
const dispatch = createEventDispatcher<TabEvents>();
const toggleTabs = (tab: Tab) => {
+46 -19
View File
@@ -3,29 +3,30 @@
import { codeStore, getStateString } from '$lib/util/state';
import {
addHistoryEntry,
autoHistoryMode,
historyModeStore,
clearHistoryData,
getPreviousState,
historyStore
historyStore,
loaderHistoryStore
} from './history';
import { notify, prompt } from '$lib/util/notify';
import { onMount } from 'svelte';
import moment from 'moment';
import type { State, Tab } from '$lib/types';
import type { HistoryType, State, Tab } from '$lib/types';
const HISTORY_SAVE_INTERVAL = 60000;
const tabSelectHandler = (message: CustomEvent<Tab>) => {
autoHistoryMode.set('timeline' === message.detail.id);
historyModeStore.set(message.detail.id as HistoryType);
};
const tabs: Tab[] = [
let tabs: Tab[] = [
{
id: 'saved',
id: 'manual',
title: 'Saved',
icon: 'far fa-bookmark'
},
{
id: 'timeline',
id: 'auto',
title: 'Timeline',
icon: 'fas fa-history'
}
@@ -38,7 +39,7 @@
addHistoryEntry({
state: $codeStore,
time: Date.now(),
auto
type: auto ? 'auto' : 'manual'
});
} else if (!auto) {
notify('State already saved.');
@@ -62,12 +63,26 @@
};
onMount(() => {
autoHistoryMode.set(false);
historyModeStore.set('manual');
setInterval(() => {
saveHistory(true);
}, HISTORY_SAVE_INTERVAL);
});
loaderHistoryStore.subscribe((entries) => {
if (entries.length > 0 && tabs.length === 2) {
tabs = [
{
id: 'loader',
title: 'Revisions',
icon: 'fab fa-git-alt'
},
...tabs
];
historyModeStore.set('loader');
}
});
let isOpen = true;
</script>
@@ -78,20 +93,30 @@
class="btn"
on:click|stopPropagation={() => saveHistory()}
title="Save current state"><i class="far fa-save" /></button>
<button
id="clearHistory"
class="btn text-red-400"
on:click|stopPropagation={() => clearHistory()}
title="Delete all saved states"><i class="fas fa-trash-alt" /></button>
{#if $historyModeStore !== 'loader'}
<button
id="clearHistory"
class="btn text-red-400"
on:click|stopPropagation={() => clearHistory()}
title="Delete all saved states"><i class="fas fa-trash-alt" /></button>
{/if}
</div>
<ul class="p-2 space-y-2 overflow-auto h-56" id="historyList">
{#if $historyStore.length > 0}
{#each $historyStore as { state, time, name }}
{#each $historyStore as { state, time, name, url, type }}
<li class="rounded p-2 shadow flex-col">
<div class="flex">
<div class="flex-1">
<div class="flex flex-col">
<span>{name}</span>
{#if url}
<a
href={url}
target="_blank"
title="Open revision in new tab"
class="hover:underline text-blue-500">{name}</a>
{:else}
<span>{name}</span>
{/if}
<span class="text-gray-400 text-sm">{relativeTime(time)}</span>
</div>
</div>
@@ -99,9 +124,11 @@
<button
class="rounded px-2 w-24 bg-green-200 hover:bg-green-300"
on:click={() => restoreHistory(state)}><i class="fas fa-undo" /> Restore</button>
<button
class="rounded px-2 w-24 bg-red-200 hover:bg-red-300"
on:click={() => clearHistory(time)}><i class="fas fa-trash-alt" /> Delete</button>
{#if type !== 'loader'}
<button
class="rounded px-2 w-24 bg-red-200 hover:bg-red-300"
on:click={() => clearHistory(time)}><i class="fas fa-trash-alt" /> Delete</button>
{/if}
</div>
</div>
</li>
+27 -11
View File
@@ -1,12 +1,12 @@
import { derived, Readable, Writable, writable, get } from 'svelte/store';
import { persist, localStorage } from '@macfja/svelte-persistent-store';
import { generateSlug } from 'random-word-slugs';
import type { HistoryEntry } from '../../types';
import type { HistoryEntry, HistoryType } from '$lib/types';
const MAX_AUTO_HISTORY_LENGTH = 30;
export const autoHistoryMode: Writable<boolean> = persist(
writable(true),
export const historyModeStore: Writable<HistoryType> = persist(
writable('manual'),
localStorage(),
'autoHistoryMode'
);
@@ -23,17 +23,30 @@ const manualHistoryStore: Writable<HistoryEntry[]> = persist(
'manualHistoryStore'
);
export const loaderHistoryStore: Writable<HistoryEntry[]> = writable([] as HistoryEntry[]);
export const historyStore: Readable<HistoryEntry[]> = derived(
[autoHistoryMode, autoHistoryStore, manualHistoryStore],
([autoMode, autoHistories, manualHistories], set) => {
set(autoMode ? autoHistories : manualHistories);
[historyModeStore, autoHistoryStore, manualHistoryStore, loaderHistoryStore],
([historyMode, autoHistories, manualHistories, loadedHistories], set) => {
if (historyMode === 'auto') {
set(autoHistories);
} else if (historyMode === 'manual') {
set(manualHistories);
} else if (historyMode === 'loader') {
set(loadedHistories);
} else {
set(autoHistories);
}
}
);
export const addHistoryEntry = (entry: HistoryEntry): void => {
if (entry.type === 'loader') {
loaderHistoryStore.update((entries) => [entry, ...entries]);
return;
}
entry.name = generateSlug(2);
if (!entry.auto) {
if (entry.type !== 'auto') {
manualHistoryStore.update((entries) => [entry, ...entries]);
return;
}
@@ -46,9 +59,12 @@ export const addHistoryEntry = (entry: HistoryEntry): void => {
};
export const clearHistoryData = (time?: number): void => {
(get(autoHistoryMode) ? autoHistoryStore : manualHistoryStore).update((entries) =>
entries.filter((entry) => time && entry.time != time)
);
(get(historyModeStore) === 'auto' ? autoHistoryStore : manualHistoryStore).update((entries) => {
if (get(historyModeStore) !== 'loader') {
entries = entries.filter((entry) => time && entry.time != time);
}
return entries;
});
};
export const getPreviousState = (auto: boolean): string => {
+22 -1
View File
@@ -29,11 +29,32 @@ export interface State {
updateEditor: boolean;
updateDiagram: boolean;
autoSync: boolean;
loader?: LoaderConfig;
}
export interface GistLoaderConfig {
url: string;
}
export interface LoadingState {
loading: boolean;
message?: string;
}
export interface FileLoaderConfig {
codeURL: string;
configURL?: string;
}
export interface LoaderConfig {
type: 'gist' | 'files';
config: GistLoaderConfig | FileLoaderConfig;
}
export type HistoryType = 'auto' | 'manual' | 'loader';
export interface HistoryEntry {
state: State;
time: number;
name?: string;
auto: boolean;
type: HistoryType;
url?: string;
}
type Loader = (url: string) => Promise<State>;
+105
View File
@@ -0,0 +1,105 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* 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';
const codeFileName = 'code.mmd';
const configFileName = 'config.json';
const isValidGist = (files: any): boolean => {
return codeFileName in files;
};
const getFileContent = async (file: any): Promise<string> => {
if (file.truncated) {
return await (await fetch(file.raw_url)).text();
}
return file.content;
};
interface GistData {
code: string;
config?: string;
author: string;
time: number;
version: string;
url: string;
}
const getGistData = async (gistURL: string): Promise<GistData> => {
// 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 { html_url, files, history } = await (
await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`)
).json();
if (isValidGist(files)) {
const code = await getFileContent(files[codeFileName]);
let config: string;
if (configFileName in files) {
config = await getFileContent(files[configFileName]);
}
const currentItem = history[0];
return {
url: `${html_url}/${currentItem.version}`,
code,
config,
author: currentItem.user.login,
time: new Date(currentItem.committed_at).getTime(),
version: (currentItem.version as string).slice(-7)
};
} else {
throw 'Invalid gist provided';
}
};
const getStateFromGist = (gist: GistData, gistURL: string = gist.url): State => {
const state: State = {
...defaultState,
code: gist.code,
loader: {
type: 'gist',
config: {
url: gistURL
}
}
};
gist.config && (state.mermaid = gist.config);
return 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);
}
};
+58
View File
@@ -0,0 +1,58 @@
import { loadGistData } from './gist';
import { updateCodeStore, defaultState } from '../state';
import type { Loader, State } from '$lib/types';
const loaders: Record<string, Loader> = {
gist: loadGistData
};
export const loadDataFromUrl = async (): Promise<void> => {
const searchParams = new URLSearchParams(window.location.search);
let state: State = defaultState;
let code: string, config: string;
let loaded = false;
const codeURL: string = searchParams.get('code');
const configURL: string = searchParams.get('config');
if (codeURL) {
code = await (await fetch(codeURL)).text();
loaded = true;
}
if (configURL) {
config = await (await fetch(configURL)).text();
} else {
config = defaultState.mermaid;
}
if (!code) {
for (const [key, value] of searchParams.entries()) {
if (key in loaders) {
try {
state = await loaders[key](value);
loaded = true;
break;
} catch (err) {
console.error(err);
}
}
}
} else {
state = {
code,
mermaid: config,
loader: {
type: 'files',
config: {
codeURL,
configURL
}
}
} as State;
}
loaded &&
updateCodeStore({
...state,
autoSync: true,
updateDiagram: true,
updateEditor: true
});
// window.location.search = '';
};
+19
View File
@@ -0,0 +1,19 @@
import { writable, Writable } from 'svelte/store';
import type { LoadingState } from '$lib/types';
const defaultLoading: LoadingState = {
loading: false
};
export const loadingStateStore: Writable<LoadingState> = writable(defaultLoading);
export const initLoading = async <T>(message: string, task: Promise<T>): Promise<T> => {
loadingStateStore.set({
loading: true,
message
});
const result: T = await task;
loadingStateStore.set({
loading: false
});
return result;
};
+1 -1
View File
@@ -4,7 +4,7 @@ import { persist, localStorage } from '@macfja/svelte-persistent-store';
import type { State } from '$lib/types';
import { saveStatistcs } from './stats';
const defaultState: State = {
export const defaultState: State = {
code: `graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
+3
View File
@@ -1,6 +1,8 @@
import type { State } from '$lib/types';
import { initURLSubscription, loadState, updateCodeStore } from './state';
import { analytics, initAnalytics } from './stats';
import { loadDataFromUrl } from './fileLoaders/loader';
import { initLoading } from './loading';
export const loadStateFromURL = (): void => {
loadState(window.location.hash.slice(1));
@@ -14,6 +16,7 @@ export const syncDiagram = (): void => {
export const initHandler = async (): Promise<void> => {
loadStateFromURL();
await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error));
syncDiagram();
initURLSubscription();
await initAnalytics();
+42
View File
@@ -2,6 +2,7 @@
import '../app.postcss';
import { base } from '$app/paths';
import { onMount } from 'svelte';
import { loadingStateStore } from '$lib/util/loading';
// 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(() => {
@@ -23,3 +24,44 @@
<main class="h-screen">
<slot />
</main>
{#if $loadingStateStore.loading}
<div
class="w-screen h-screen z-50 absolute left-0 top-0 bg-gray-600 opacity-50 flex align-middle justify-center">
<div class="text-indigo-100 text-4xl font-bold my-auto">
<div class="loader mx-auto" />
<div>{$loadingStateStore.message}</div>
</div>
</div>
{/if}
<style>
.loader {
border: 0.45em solid #f3f3f3;
border-radius: 50%;
border-top: 0.45em solid #6365f1;
width: 3em;
height: 3em;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
}
/* Safari */
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
+1 -1
View File
@@ -2,7 +2,7 @@
"compilerOptions": {
"moduleResolution": "node",
"module": "es2020",
"lib": ["es2020", "ESNext"],
"lib": ["es2020", "ESNext", "DOM.Iterable"],
"target": "es2019",
/**
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript