Merge branch 'master' into develop

* master: (26 commits)
  Add tests for themes
  Fix: Light theme not being set by default
  Apply suggestions from code review
  chore: rename theme
  fix: Mermaid Theme
  fix: menu width
  fix: Use span instead of a in menu
  Update src/lib/components/navbar.svelte
  [ImgBot] Optimize images
  Add powered by netlify
  add netlify badge
  test deploy
  update test
  update
  fix: resize CSS
  fix: ResizeHandler
  fix: Auto sync margin
  fix: Scroll
  chore: Update daisyUI
  fix: Aqua dark
  ...
This commit is contained in:
Sidharth Vinod
2021-09-20 23:34:30 +05:30
25 changed files with 353 additions and 138 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
[![Mermaid Live Editor](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/detailed/2ckppp/master&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/2ckppp/runs) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
[![Mermaid Live Editor](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/detailed/2ckppp/master&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/2ckppp/runs) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)[![Netlify Status](https://api.netlify.com/api/v1/badges/27fa023d-7c73-4a3f-9791-b3b657a47100/deploy-status)](https://app.netlify.com/sites/mermaidjs/deploys)
# Contributors are welcome!
@@ -17,7 +17,7 @@ Edit, preview and share mermaid charts/diagrams.
## Live demo
You can try out a live version [here](https://mermaid-js.github.io/mermaid-live-editor/).
You can try out a live version [here](https://mermaid.live/).
## Docker
+57
View File
@@ -0,0 +1,57 @@
describe('Test themes', () => {
describe('Test light themes', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit', {
onBeforeLoad(win) {
cy.stub(win, 'matchMedia')
.callThrough()
.withArgs('(prefers-color-scheme: dark)')
.returns({
matches: false
});
}
});
cy.contains('Change Theme').click();
});
it('should set light theme as default', () => {
cy.contains('light').parent().should('have.class', 'bordered');
cy.contains('dark').parent().should('not.have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
it('should change themes when clicked', () => {
cy.contains('light').parent().should('have.class', 'bordered');
cy.contains('cupcake').click();
cy.contains('cupcake').parent().should('have.class', 'bordered');
cy.contains('light').parent().should('not.have.class', 'bordered');
cy.contains('dark').parent().should('not.have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
});
describe('Test dark mode', () => {
// eslint-disable-next-line mocha/no-hooks-for-single-case
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit', {
onBeforeLoad(win) {
cy.stub(win, 'matchMedia')
.callThrough()
.withArgs('(prefers-color-scheme: dark)')
.returns({
matches: true
});
}
});
cy.contains('Change Theme').click();
});
it('should set dark theme as default', () => {
cy.contains('light').parent().should('not.have.class', 'bordered');
cy.contains('dark').parent().should('have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
});
});
+25 -1
View File
@@ -16,7 +16,7 @@ module.exports = {
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}"
}
},
"__version": "8.3.1",
"__version": "8.4.0",
"Auto sync tests": {
"should dim diagram when code is edited": {
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}"
@@ -24,5 +24,29 @@ module.exports = {
"should not dim diagram when code is in sync": {
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}"
}
},
"Test themes": {
"should set light theme as default": {
"1": "{\"theme\":\"light\",\"isDark\":false}"
},
"should change themes when clicked": {
"1": "{\"theme\":\"cupcake\",\"isDark\":false}"
},
"should set dark theme as default": {
"1": "{\"theme\":\"dark\",\"isDark\":true}"
},
"Test light themes": {
"should set light theme as default": {
"1": "{\"theme\":\"light\",\"isDark\":false}"
},
"should change themes when clicked": {
"1": "{\"theme\":\"cupcake\",\"isDark\":false}"
}
},
"Test dark mode": {
"should set dark theme as default": {
"1": "{\"theme\":\"dark\",\"isDark\":true}"
}
}
}
}
+1
View File
@@ -51,6 +51,7 @@
"@analytics/google-analytics": "^0.5.3",
"@macfja/svelte-persistent-store": "^1.2.0",
"analytics": "^0.7.14",
"daisyui": "^1.14.0",
"js-base64": "^3.7.1",
"mermaid": "^8.12.1",
"moment": "^2.29.1",
+3 -11
View File
@@ -2,17 +2,9 @@
@tailwind components;
@tailwind utilities;
.nav-btn {
@apply lg:p-2 py-3 px-0 block border-b-2 border-transparent hover:border-white;
}
.btn {
@apply bg-indigo-500 hover:bg-indigo-700 rounded px-4 shadow;
.input {
@apply flex-1 border-primary border-solid border-2 rounded;
}
.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;
@apply btn btn-primary;
}
+28 -14
View File
@@ -153,39 +153,53 @@
<div class="flex flex-wrap gap-2 m-2">
{#if isClipboardAvailable()}
<button class="action-btn w-full" on:click={onCopyClipboard}
><i class="far fa-copy" /> Copy Image to clipboard
><i class="far fa-copy mr-2" /> Copy Image to clipboard
</button>
{/if}
<button class="action-btn flex-auto" on:click={onDownloadPNG}>
<i class="fas fa-download" /> PNG
<i class="fas fa-download mr-2" /> PNG
</button>
<button class="action-btn flex-auto" on:click={onDownloadSVG}>
<i class="fas fa-download" /> SVG
<i class="fas fa-download mr-2" /> SVG
</button>
<button class="action-btn flex-auto">
<a target="_blank" href={iUrl}><i class="fas fa-external-link-alt" /> PNG</a>
<a target="_blank" href={iUrl}><i class="fas fa-external-link-alt mr-2" /> PNG</a>
</button>
<button class="action-btn flex-auto">
<a target="_blank" href={svgUrl}><i class="fas fa-external-link-alt" /> SVG</a>
<a target="_blank" href={svgUrl}><i class="fas fa-external-link-alt mr-2" /> SVG</a>
</button>
<div class="flex gap-2 items-center">
PNG size
<input type="radio" value="auto" id="autosize" bind:group={imagemodeselected} />
<label for="autosize">Auto</label>
<input type="radio" value="width" id="width-active" bind:group={imagemodeselected} />
<label for="width">Width</label>
<input type="radio" value="height" id="height-active" bind:group={imagemodeselected} />
<label for="height">Height</label>
<label for="autosize">
<input type="radio" value="auto" id="autosize" bind:group={imagemodeselected} /> Auto
</label>
<label for="width">
<input type="radio" value="width" id="width" bind:group={imagemodeselected} /> Width
</label>
<label for="height">
<input type="radio" value="height" id="height" bind:group={imagemodeselected} /> Height
</label>
{#if imagemodeselected !== 'auto'}
<input id="height" type="number" min="3" max="10000" bind:value={userimagesize} />
<input
id="height"
class="input"
type="number"
min="3"
max="10000"
bind:value={userimagesize} />
{/if}
</div>
<div class="w-full flex gap-2 items-center">
<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>
<button class="btn btn-primary btn-md flex-auto" on:click={onCopyMarkdown}>
Copy Markdown
</button>
</label>
</div>
@@ -197,7 +211,7 @@
bind:value={gistURL}
placeholder="Enter Gist URL" />
<label for="gist">
<button class="btn text-white flex-auto" on:click={loadGist}> Load Gist </button>
<button class="btn btn-primary btn-md flex-auto" on:click={loadGist}> Load Gist </button>
</label>
</div>
{#if isNetlify}
+5 -4
View File
@@ -7,21 +7,22 @@
export let tabs: Tab[] = [];
export let title: string;
$: isOpen = isCloseable ? isOpen : true;
$: isTabsShown = isOpen && tabs.length > 0;
</script>
<div class="bg-white rounded overflow-hidden shadow m-2 flex-grow flex flex-col">
<div class="card rounded overflow-hidden m-2 flex-grow flex flex-col shadow-2xl">
<div
class="bg-indigo-400 border-gray-400 p-2 flex-none cursor-pointer"
class="bg-primary p-2 {isTabsShown ? 'pb-0' : ''} flex-none cursor-pointer"
on:click={() => (isOpen = !isOpen)}>
<div class="flex justify-between">
<Tabs on:select {tabs} bind:isOpen {title} {isCloseable} />
<div class="flex gap-x-4 items-center text-white">
<div class="flex gap-x-4 items-center {isTabsShown ? '-mt-2' : ''}">
<slot name="actions" />
</div>
</div>
</div>
{#if isOpen}
<div class="flex-grow overflow-auto" transition:slide>
<div class="card-body p-0 flex-grow overflow-auto text-base-content" transition:slide>
<slot />
</div>
{/if}
+8 -13
View File
@@ -17,25 +17,20 @@
</script>
<div class="flex cursor-default">
<span class="text-white mr-2 font-semibold" on:click|stopPropagation={() => (isOpen = !isOpen)}>
<span class="mr-2 font-semibold" on:click|stopPropagation={() => (isOpen = !isOpen)}>
{#if isCloseable}
<i class="fas fa-chevron-right icon" class:isOpen />
{/if}
{title}</span>
{#if isOpen && tabs}
<ul class="flex flex-wrap flex-row" transition:fade>
<ul class="tabs" transition:fade>
{#each tabs as tab}
<li class="mr-2 last:mr-0 w-28 h-6 flex-auto text-center">
<div
class="text cursor-pointer font-semibold min-w-16 w-auto px-2 py-1 -mb-4 rounded-t flex justify-center items-center leading-normal {activeTabID ===
tab.id
? 'text-indigo-500 bg-white border-white'
: 'text-white bg-indigo-500 border-indigo-500 hover:bg-indigo-600'}"
on:click|stopPropagation={() => toggleTabs(tab)}>
<i class="{tab.icon} mr-1" />
{tab.title}
</div>
</li>
<div
class="tab tab-lifted text-primary-content {activeTabID === tab.id ? 'tab-active' : ''}"
on:click|stopPropagation={() => toggleTabs(tab)}>
<i class="mr-1 {tab.icon}" />
{tab.title}
</div>
{/each}
</ul>
{/if}
+7 -2
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import type { EditorEvents } from '$lib/types';
import { codeStore } from '$lib/util/state';
import { themeStore } from '$lib/util/theme';
import type monaco from 'monaco-editor';
import { createEventDispatcher, onMount } from 'svelte';
import { initEditor } from './util';
@@ -17,7 +18,7 @@
minimap: {
enabled: false
},
theme: 'mermaidTheme',
theme: 'mermaid',
overviewRulerLanes: 0
};
export let errorMarkers: monaco.editor.IMarkerData[] = [];
@@ -33,6 +34,10 @@
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', errorMarkers);
}
themeStore.subscribe(({ isDark }) => {
editor && Monaco?.editor.setTheme(isDark ? 'mermaid-dark' : 'mermaid');
});
const dispatch = createEventDispatcher<EditorEvents>();
const loadMonaco = async () => {
let i = 0;
@@ -62,7 +67,7 @@
text
});
});
Monaco?.editor.setTheme($themeStore.isDark ? 'mermaid-dark' : 'mermaid');
const resizeObserver = new ResizeObserver((entries) => {
editor.layout({
height: entries[0].contentRect.height,
+11 -2
View File
@@ -365,9 +365,18 @@ export const initEditor = (monacoEditor): void => {
}
});
monacoEditor.editor.defineTheme('mermaidTheme', {
monacoEditor.editor.defineTheme('mermaid-dark', {
base: 'vs-dark',
inherit: true,
rules: [
{ token: 'typeKeyword', foreground: '9650c8', fontStyle: 'bold' },
{ token: 'transition', foreground: '008800', fontStyle: 'bold' }
]
});
monacoEditor.editor.defineTheme('mermaid', {
base: 'vs',
inherit: false,
inherit: true,
rules: [
{ token: 'typeKeyword', foreground: '9650c8', fontStyle: 'bold' },
{ token: 'keyword', foreground: '649696' },
+8 -10
View File
@@ -90,13 +90,13 @@
<div slot="actions">
<button
id="saveHistory"
class="btn"
class="btn btn-xs btn-success w-12"
on:click|stopPropagation={() => saveHistory()}
title="Save current state"><i class="far fa-save" /></button>
{#if $historyModeStore !== 'loader'}
<button
id="clearHistory"
class="btn text-red-400"
class="btn btn-xs btn-error w-12"
on:click|stopPropagation={() => clearHistory()}
title="Delete all saved states"><i class="fas fa-trash-alt" /></button>
{/if}
@@ -107,7 +107,7 @@
<li class="rounded p-2 shadow flex-col">
<div class="flex">
<div class="flex-1">
<div class="flex flex-col">
<div class="flex flex-col text-base-content">
{#if url}
<a
href={url}
@@ -121,20 +121,18 @@
</div>
</div>
<div class="flex gap-2 content-center">
<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="btn btn-success" on:click={() => restoreHistory(state)}
><i class="fas fa-undo mr-1" />Restore</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>
<button class="btn btn-error" on:click={() => clearHistory(time)}
><i class="fas fa-trash-alt mr-1" />Delete</button>
{/if}
</div>
</div>
</li>
{/each}
{:else}
<div class="m-2 text-gray-600">
<div class="m-2">
No items in History<br />
Click the Save button to save current state and restore it later.<br />
Timeline will automatically be saved every minute.
+21 -19
View File
@@ -7,6 +7,8 @@
</script>
<script lang="ts">
import Theme from './theme.svelte';
interface Link {
title: string;
href: string;
@@ -37,11 +39,12 @@
];
</script>
<header class="p-2 bg-indigo-400 text-white font-bold flex flex-wrap items-center">
<div class="flex-1 flex justify-between items-center">
<a href="/">Mermaid<span class="text-xs font-thin">v{version}</span> Live Editor</a>
<div class="navbar mb-2 shadow-lg bg-primary">
<div class="flex-1 px-2 mx-2">
<span class="text-lg font-bold">
<a href="/">Mermaid<span class="text-xs font-thin">v{version}</span> Live Editor</a>
</span>
</div>
<label for="menu-toggle" class="pointer-cursor lg:hidden block"
><svg
class="fill-current "
@@ -53,28 +56,27 @@
></label>
<input class="hidden" type="checkbox" id="menu-toggle" />
<Theme />
<div class="hidden lg:flex lg:items-center lg:w-auto w-full" id="menu">
<nav>
<ul class="lg:flex items-center justify-between text-base pt-4 lg:pt-0">
{#each links as { title, href, icon }}
<li>
<a class="nav-btn" target="_blank" {href}>
{#if icon}
<i class={icon} />
{/if}
{title}</a>
</li>
{/each}
</ul>
</nav>
<ul class="lg:flex items-center justify-between text-base pt-4 lg:pt-0">
{#each links as { title, href, icon }}
<li>
<a class="btn btn-ghost" target="_blank" {href}>
{#if icon}
<i class={icon} />
{/if}
{title}</a>
</li>
{/each}
</ul>
</div>
</header>
</div>
<style>
#menu-toggle:checked + #menu {
display: block;
}
header {
.navbar {
z-index: 10000;
}
</style>
+2 -1
View File
@@ -108,7 +108,8 @@ merge newbranch
<Card title="Sample Diagrams" isOpen={false}>
<div class="flex gap-2 flex-wrap p-2">
{#each Object.keys(samples) as sample}
<button class="action-btn" on:click={() => loadSampleDiagram(sample)}>{sample}</button>
<button class="btn btn-primary normal-case btn-sm" on:click={() => loadSampleDiagram(sample)}
>{sample}</button>
{/each}
</div>
</Card>
+59
View File
@@ -0,0 +1,59 @@
<script>
import { setTheme, themeStore } from '$lib/util/theme';
const themes = [
'🌝 light',
'🌚 dark',
'🧁 cupcake',
'🐝 bumblebee',
'✳️ emerald',
'🏢 corporate',
'🌃 synthwave',
'👴 retro',
'🤖 cyberpunk',
'🌸 valentine',
'🎃 halloween',
'🌷 garden',
'🌲 forest',
'🐟 aqua',
'👓 lofi',
'🖍 pastel',
'🧚‍♀️ fantasy',
'📝 wireframe',
'🏴 black',
'💎 luxury',
'🧛‍♂️ dracula'
];
</script>
<div class="hidden lg:block dropdown">
<div tabindex="0" class="btn btn-ghost ">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="inline-block w-6 h-6 stroke-current md:mr-2"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" /></svg>
<span class="hidden md:inline"> Change Theme </span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1792 1792"
class="inline-block w-4 h-4 ml-1 fill-current"
><path
d="M1395 736q0 13-10 23l-466 466q-10 10-23 10t-23-10l-466-466q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l393 393 393-393q10-10 23-10t23 10l50 50q10 10 10 23z" /></svg>
</div>
<div
class="mt-14 overflow-y-auto shadow-2xl top-px dropdown-content h-96 w-56 bg-base-200 text-base-content">
<ul tabindex="0" class="p-4 menu compact">
{#each themes as theme}
<li class={theme.includes($themeStore.theme) ? 'bordered' : ''}>
<span class="btn btn-ghost justify-start" on:click={() => setTheme(theme)}>{theme}</span>
</li>
{/each}
</ul>
</div>
</div>
+4 -3
View File
@@ -8,6 +8,7 @@
let code = '';
let config = '';
let container: HTMLDivElement;
let view: HTMLDivElement;
let error = false;
let outOfSync = false;
let manualUpdate = true;
@@ -26,13 +27,13 @@
}
code = state.code;
config = state.mermaid;
const scroll = container.parentElement.parentElement.parentElement.scrollTop;
const scroll = view.parentElement.scrollTop;
delete container.dataset.processed;
mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid)));
mermaid.render('graph-div', code, (svgCode) => {
container.innerHTML = svgCode;
});
container.parentElement.parentElement.parentElement.scrollTop = scroll;
view.parentElement.scrollTop = scroll;
error = false;
} else if (manualUpdate) {
manualUpdate = false;
@@ -55,7 +56,7 @@
});
</script>
<div id="view" class="p-2" class:error class:outOfSync>
<div id="view" bind:this={view} class="p-2" class:error class:outOfSync>
<div id="container" bind:this={container} class="flex-1 overflow-auto" />
</div>
+15 -4
View File
@@ -2,7 +2,7 @@ import { writable, get, derived } from 'svelte/store';
import { toBase64, fromBase64 } from 'js-base64';
import { persist, localStorage } from '@macfja/svelte-persistent-store';
import type { State } from '$lib/types';
import { saveStatistcs } from './stats';
import { saveStatistics } from './stats';
export const defaultState: State = {
code: `graph TD
@@ -33,7 +33,7 @@ export const loadState = (data: string): void => {
let state: State;
try {
const stateStr = fromBase64(data);
console.log(`Tring to load state: ${stateStr}`);
console.log(`Trying to load state: ${stateStr}`);
state = JSON.parse(stateStr);
const mermaidConfig =
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
@@ -66,12 +66,12 @@ export const updateCodeStore = (newState: State): void => {
let prompted = false;
export const updateCode = (code: string, updateEditor: boolean, updateDiagram = false): void => {
saveStatistcs(code);
saveStatistics(code);
const lines = (code.match(/\n/g) || '').length + 1;
if (lines > 50 && !prompted && get(codeStore).autoSync) {
const turnOff = confirm(
'Long diagram deteced. Turn off Auto Sync? Click the sync logo to manually sync.'
'Long diagram detected. Turn off Auto Sync? Click the sync logo to manually sync.'
);
prompted = true;
if (turnOff) {
@@ -92,6 +92,17 @@ export const updateConfig = (config: string, updateEditor: boolean): void => {
});
};
export const toggleDarkTheme = (dark: boolean): void => {
codeStore.update((state) => {
const config = JSON.parse(state.mermaid);
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
config.theme = dark ? 'dark' : 'default';
}
return { ...state, mermaid: JSON.stringify(config, null, 2), updateEditor: true };
});
};
export const initURLSubscription = (): void => {
base64State.subscribe((state: string) => {
history.replaceState(undefined, undefined, `#${state}`);
+2 -2
View File
@@ -34,10 +34,10 @@ const detectType = (text: string): string => {
// manual debounce
let timeout;
export const saveStatistcs = (graph: string): void => {
export const saveStatistics = (graph: string): void => {
if (analytics) {
clearTimeout(timeout);
// Only save statistcs after a 5 sec delay
// Only save statistics after a 5 sec delay
timeout = setTimeout(function () {
const graphType = detectType(graph);
console.debug('ga:', 'send', 'event', 'render', graphType);
+36
View File
@@ -0,0 +1,36 @@
import { writable } from 'svelte/store';
import type { Writable } from 'svelte/store';
import { persist, localStorage } from '@macfja/svelte-persistent-store';
export interface ThemeConfig {
isDark: boolean;
theme?: string;
}
export const themeStore: Writable<ThemeConfig> = persist(
writable({
isDark: false
}),
localStorage(),
'themeStore'
);
const darkThemes = [
'dark',
'synthwave',
'halloween',
'aqua',
'forest',
'luxury',
'black',
'dracula'
];
export const setTheme = (theme: string): void => {
if (theme.includes(' ')) {
theme = theme.split(' ')[1].trim();
}
const isDark = darkThemes.includes(theme);
console.log('Setting theme', theme);
themeStore.set({ theme, isDark });
};
+17 -1
View File
@@ -3,6 +3,9 @@
import { base } from '$app/paths';
import { onMount } from 'svelte';
import { loadingStateStore } from '$lib/util/loading';
import { setTheme, themeStore } from '$lib/util/theme';
import { toggleDarkTheme } from '$lib/util/state';
// 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(() => {
@@ -18,10 +21,23 @@
console.log('Service worker registration failed, error:', error);
});
}
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
if ($themeStore.theme === undefined) {
setTheme(isDarkMode ? 'dark' : 'light');
}
themeStore.subscribe(({ theme, isDark }) => {
if (theme) {
document.getElementsByTagName('html')[0].setAttribute('data-theme', theme);
toggleDarkTheme(isDark);
}
});
});
</script>
<main class="h-screen">
<main class="h-screen text-primary-content">
<slot />
</main>
+28 -26
View File
@@ -121,25 +121,32 @@
});
</script>
<div class="h-full flex flex-col overflow-hidden bg-gray-100">
<div class="h-full flex flex-col overflow-hidden">
<Navbar />
<div class="flex-1 flex overflow-hidden">
<div class="hidden md:flex flex-col" id="editorPane" style="width: 40%">
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} title="Mermaid">
<div slot="actions">
{#if !$codeStore.autoSync}
<button
class="bg-indigo-500 hover:bg-indigo-700 rounded px-4 mx-2"
title="Sync Diagram"
data-cy="sync"
on:click={syncDiagram}><i class="fas fa-sync" /></button>
{/if}
<label
for="autoSync"
style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
<input type="checkbox" id="autoSync" bind:checked={$codeStore.autoSync} />
Auto sync
</label>
<div class="flex flex-row items-center">
{#if !$codeStore.autoSync}
<button
class="btn btn-secondary btn-xs"
title="Sync Diagram"
data-cy="sync"
on:click={syncDiagram}><i class="fas fa-sync" /></button>
{/if}
<div class="form-control">
<label class="cursor-pointer label" for="autoSync">
<input
type="checkbox"
class="toggle toggle-primary mr-1"
id="autoSync"
bind:checked={$codeStore.autoSync} />
<span> Auto sync</span>
</label>
</div>
</div>
</div>
<Editor on:update={updateHandler} {language} bind:text {errorMarkers} />
@@ -151,20 +158,20 @@
<Actions />
</div>
</div>
<div id="resizeHandler" />
<div id="resizeHandler" class="hidden md:block" />
<div class="flex-1 flex flex-col overflow-hidden">
<Card title="Diagram" isCloseable={false}>
<button
slot="actions"
class="btn"
class="btn btn-primary btn-xs shadow-lg"
title="View diagram in new page"
on:click|stopPropagation={() => viewDiagram()}><i class="far fa-eye" /> View</button>
on:click|stopPropagation={() => viewDiagram()}><i class="far fa-eye mr-2" /> View</button>
<div class="flex-1 overflow-auto">
<View />
</div>
</Card>
<div class="md:hidden bg-white rounded shadow p-2 mx-2">
<div class="md:hidden rounded shadow p-2 mx-2">
Code editing not supported on mobile. Please use a desktop browser.
</div>
</div>
@@ -183,19 +190,14 @@
top: 0;
content: '';
position: absolute;
background-color: #ccc;
background-color: hsla(var(--b3));
margin-left: -1px;
transition-duration: 0.2s;
}
#resizeHandler:hover::after {
margin-left: -2px;
background-color: #818cf8;
background-color: hsla(var(--p));
width: 4px;
}
@media screen and (max-width: 768px) {
#resizeHandler {
display: none;
}
}
</style>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 47 KiB

+3 -17
View File
@@ -1,21 +1,7 @@
const { tailwindExtractor } = require('tailwindcss/lib/lib/purgeUnusedStyles');
module.exports = {
mode: 'aot',
purge: {
content: ['./src/**/*.{html,js,svelte,ts}'],
options: {
defaultExtractor: (content) => [
// If this stops working, please open an issue at https://github.com/svelte-add/tailwindcss/issues rather than bothering Tailwind Labs about it
...tailwindExtractor(content),
// Match Svelte class: directives (https://github.com/tailwindlabs/tailwindcss/discussions/1731)
...[...content.matchAll(/(?:class:)*([\w\d-/:%.]+)/gm)].map(
([_match, group, ..._rest]) => group
)
]
},
safelist: [/^svelte-[\d\w]+$/]
},
mode: 'jit',
plugins: [require('daisyui')],
purge: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {}
},
+11 -6
View File
@@ -39,7 +39,7 @@
"@analytics/storage-utils@^0.2.12":
version "0.2.12"
resolved "https://registry.yarnpkg.com/@analytics/storage-utils/-/storage-utils-0.2.12.tgz#cae7597934b76716d8a6a4f6696fb04cffb05861"
resolved "https://registry.npmjs.org/@analytics/storage-utils/-/storage-utils-0.2.12.tgz"
integrity sha512-cuQuV5sgk1vuIwFUFJ5VJv0EsDL84SS4enuCyY9JEcE24wJeC6iUEy6O7OJvVssYpGJsW+i1XSgwCDJTPgkZgg==
dependencies:
"@analytics/cookie-utils" "^0.2.6"
@@ -49,7 +49,7 @@
"@analytics/type-utils@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@analytics/type-utils/-/type-utils-0.3.1.tgz#49db3a95fd45864e30f6ad43dddf5b78fba562d6"
resolved "https://registry.npmjs.org/@analytics/type-utils/-/type-utils-0.3.1.tgz"
integrity sha512-9Ej6c9ulj5DNE29eFk3s49/1b89M8mbQ7BoJEQAQg/k86HOVrAEWQUScr359WVSOyGNBsSYE1qILC3XFTTtgtw==
"@babel/code-frame@7.12.11":
@@ -1415,6 +1415,11 @@ dagre@^0.8.5:
graphlib "^2.1.8"
lodash "^4.17.15"
daisyui@^1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-1.14.0.tgz#1d63658c0dd87f9795d333e7068cedbb0b0a52a6"
integrity sha512-50ccXdJMi0TS7raItKfUOJppPRvluOUCO4m6i7QPOVOdDZVP6awjqUoGmLzqFepO6b6QpVotswDvaXIKSZ/2aw==
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz"
@@ -1759,7 +1764,7 @@ eslint-visitor-keys@^2.0.0:
eslint@^7.32.0:
version "7.32.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
resolved "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz"
integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
dependencies:
"@babel/code-frame" "7.12.11"
@@ -2666,7 +2671,7 @@ lines-and-columns@^1.1.6:
lint-staged@^11.1.2:
version "11.1.2"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.1.2.tgz#4dd78782ae43ee6ebf2969cad9af67a46b33cd90"
resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-11.1.2.tgz"
integrity sha512-6lYpNoA9wGqkL6Hew/4n1H6lRqF3qCsujVT0Oq5Z4hiSAM7S6NksPJ3gnr7A7R52xCtiZMcEUNNQ6d6X5Bvh9w==
dependencies:
chalk "^4.1.1"
@@ -3579,7 +3584,7 @@ ramda@^0.27.1, ramda@~0.27.1:
random-word-slugs@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/random-word-slugs/-/random-word-slugs-0.0.5.tgz#6ccd6c7ea320be9fbc19507f8c3a7d4a970ff61f"
resolved "https://registry.npmjs.org/random-word-slugs/-/random-word-slugs-0.0.5.tgz"
integrity sha512-KwelmWsWHiMl3MKauB5usAIPg2FgwAku+FVYEuf32yyhZmEh3Fq4nXBxeUAgXB2F+G/HTeDsqXsmuupmOMnjRg==
randombytes@^2.1.0:
@@ -4181,7 +4186,7 @@ tslib@^1.8.1, tslib@^1.9.0:
tslib@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tsutils@^3.21.0: