fix: Use McWrapper, fix PNG export
This commit is contained in:
@@ -4,10 +4,10 @@
|
||||
import { Input } from '$/components/ui/input';
|
||||
import { Separator } from '$/components/ui/separator';
|
||||
import * as ToggleGroup from '$/components/ui/toggle-group';
|
||||
import { env } from '$/util/env';
|
||||
import { browser } from '$app/environment';
|
||||
import { waitForRender } from '$lib/util/autoSync';
|
||||
import { env } from '$lib/util/env';
|
||||
import { stateStore, urlsStore } from '$lib/util/state';
|
||||
import { inputStateStore, stateStore, urlsStore } from '$lib/util/state';
|
||||
import { logEvent } from '$lib/util/stats';
|
||||
import { version as FAVersion } from '@fortawesome/fontawesome-free/package.json';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -19,7 +19,6 @@
|
||||
import CopyInput from './CopyInput.svelte';
|
||||
|
||||
const FONT_AWESOME_URL = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${FAVersion}/css/all.min.css`;
|
||||
const { krokiRendererUrl } = env;
|
||||
|
||||
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
|
||||
|
||||
@@ -62,6 +61,8 @@ ${svgString}`);
|
||||
};
|
||||
|
||||
const exportImage = async (event: Event, exporter: Exporter) => {
|
||||
$inputStateStore.panZoom = false;
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
await waitForRender();
|
||||
if (document.querySelector('.outOfSync')) {
|
||||
throw new Error('Diagram is out of sync');
|
||||
@@ -74,8 +75,6 @@ ${svgString}`);
|
||||
}
|
||||
|
||||
const box = svg.getBoundingClientRect();
|
||||
canvas.width = box.width;
|
||||
canvas.height = box.height;
|
||||
|
||||
if (imageSizeMode === 'width') {
|
||||
const ratio = box.height / box.width;
|
||||
@@ -85,6 +84,10 @@ ${svgString}`);
|
||||
const ratio = box.width / box.height;
|
||||
canvas.width = imageSize * ratio;
|
||||
canvas.height = imageSize;
|
||||
} else {
|
||||
const multiplier = 2;
|
||||
canvas.width = box.width * multiplier;
|
||||
canvas.height = box.height * multiplier;
|
||||
}
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
@@ -96,7 +99,10 @@ ${svgString}`);
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const image = new Image();
|
||||
image.addEventListener('load', exporter(context, image));
|
||||
image.addEventListener('load', () => {
|
||||
exporter(context, image)();
|
||||
$inputStateStore.panZoom = true;
|
||||
});
|
||||
image.src = `data:image/svg+xml;base64,${getBase64SVG(svg, canvas.width, canvas.height)}`;
|
||||
|
||||
event.stopPropagation();
|
||||
@@ -226,7 +232,7 @@ ${svgString}`);
|
||||
{@render dualActionButton('PNG', onDownloadPNG, $urlsStore.png)}
|
||||
{@render dualActionButton('SVG', onDownloadSVG, $urlsStore.svg)}
|
||||
|
||||
{#if krokiRendererUrl}
|
||||
{#if env.krokiRendererUrl}
|
||||
<a target="_blank" rel="noreferrer" class="flex-grow" href={$urlsStore.kroki}>
|
||||
<Button class="action-btn w-full">
|
||||
<ExternalLinkIcon class="mr-2" /> Kroki
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import McTooltip from '$/components/MCTooltip.svelte';
|
||||
import McWrapper from '$/components/McWrapper.svelte';
|
||||
import { Button } from '$/components/ui/button';
|
||||
import { env } from '$/util/env';
|
||||
import type { EditorMode } from '$lib/types';
|
||||
@@ -147,14 +147,12 @@
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if env.isEnabledMermaidChartLinks}
|
||||
<McTooltip>
|
||||
<Button variant="accent" size="sm" href={$urlsStore.mermaidChart.save}>
|
||||
<img class="size-4" src="./mermaidchart-logo.svg" alt="Mermaid Chart" />
|
||||
AI Repair
|
||||
</Button>
|
||||
</McTooltip>
|
||||
{/if}
|
||||
<McWrapper>
|
||||
<Button variant="accent" size="sm" href={$urlsStore.mermaidChart.save}>
|
||||
<img class="size-4" src="./mermaidchart-logo.svg" alt="Mermaid Chart" />
|
||||
AI Repair
|
||||
</Button>
|
||||
</McWrapper>
|
||||
</div>
|
||||
<output class="max-h-32 overflow-auto bg-muted p-2" name="mermaid-error" for="editor">
|
||||
<pre>{$stateStore.error?.toString()}</pre>
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<script lang="ts">
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import type { Snippet } from 'svelte';
|
||||
import ExternalLinkIcon from '~icons/material-symbols/open-in-new-rounded';
|
||||
|
||||
let { children }: { children: Snippet } = $props();
|
||||
</script>
|
||||
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root delayDuration={100}>
|
||||
<Tooltip.Trigger>
|
||||
{@render children()}
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content>
|
||||
<div class="flex items-center gap-2">
|
||||
<ExternalLinkIcon />
|
||||
<span class="flex items-center gap-1">
|
||||
Opens a new tab in <div class="text-accent">MermaidChart.com</div>
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
@@ -1,8 +1,7 @@
|
||||
<script lang="ts">
|
||||
import McTooltip from '$/components/MCTooltip.svelte';
|
||||
import McWrapper from '$/components/McWrapper.svelte';
|
||||
import * as Popover from '$/components/ui/popover';
|
||||
import { Switch } from '$/components/ui/switch';
|
||||
import { env } from '$/util/env';
|
||||
import { urlsStore } from '$/util/state';
|
||||
import { cn } from '$/utils';
|
||||
import { mode, setMode } from 'mode-watcher';
|
||||
@@ -38,44 +37,39 @@
|
||||
{/snippet}
|
||||
|
||||
<Popover.Root>
|
||||
<Popover.Trigger class="flex shrink-0 items-center gap-0">
|
||||
<Popover.Trigger class="shrink-0">
|
||||
<img class="size-6" src="./favicon.svg" alt="Mermaid Live Editor" />
|
||||
</Popover.Trigger>
|
||||
<Popover.Content align="start" class="overflow-hidden p-0" sideOffset={16}>
|
||||
<div class="flex flex-col">
|
||||
{#each menuItems as item}
|
||||
{@render menuItem(item)}
|
||||
{/each}
|
||||
<div
|
||||
class="flex items-center justify-between border-b bg-muted px-3 py-2 hover:bg-background">
|
||||
<span class="flex items-center gap-2">
|
||||
<div class="grid">
|
||||
{#key $mode}
|
||||
<div transition:scale class="col-start-1 row-start-1">
|
||||
{#if $mode === 'dark'}
|
||||
<MoonIcon />
|
||||
{:else}
|
||||
<SunIcon />
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
</div>
|
||||
Dark Mode
|
||||
</span>
|
||||
<Switch
|
||||
checked={$mode === 'dark'}
|
||||
onCheckedChange={(dark) => setMode(dark ? 'dark' : 'light')} />
|
||||
</div>
|
||||
{#if env.isEnabledMermaidChartLinks}
|
||||
<McTooltip>
|
||||
{@render menuItem({
|
||||
label: 'Edit in Playground',
|
||||
icon: PlaygroundIcon,
|
||||
href: $urlsStore.mermaidChart.playground,
|
||||
class: 'text-accent bg-background hover:bg-background/50'
|
||||
})}
|
||||
</McTooltip>
|
||||
{/if}
|
||||
<Popover.Content align="start" class="flex flex-col overflow-hidden p-0" sideOffset={16}>
|
||||
{#each menuItems as item}
|
||||
{@render menuItem(item)}
|
||||
{/each}
|
||||
<div class="flex items-center justify-between border-b bg-muted px-3 py-2 hover:bg-background">
|
||||
<span class="flex items-center gap-2">
|
||||
<div class="grid">
|
||||
{#key $mode}
|
||||
<div transition:scale class="col-start-1 row-start-1">
|
||||
{#if $mode === 'dark'}
|
||||
<MoonIcon />
|
||||
{:else}
|
||||
<SunIcon />
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
</div>
|
||||
Dark Mode
|
||||
</span>
|
||||
<Switch
|
||||
checked={$mode === 'dark'}
|
||||
onCheckedChange={(dark) => setMode(dark ? 'dark' : 'light')} />
|
||||
</div>
|
||||
<McWrapper>
|
||||
{@render menuItem({
|
||||
label: 'Edit in Playground',
|
||||
icon: PlaygroundIcon,
|
||||
href: $urlsStore.mermaidChart.playground,
|
||||
class: 'text-accent bg-background hover:bg-background/50'
|
||||
})}
|
||||
</McWrapper>
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { env } from '$/util/env';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import type { Snippet } from 'svelte';
|
||||
import ExternalLinkIcon from '~icons/material-symbols/open-in-new-rounded';
|
||||
|
||||
let { children }: { children: Snippet } = $props();
|
||||
</script>
|
||||
|
||||
{#if env.isEnabledMermaidChartLinks}
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root delayDuration={100}>
|
||||
<Tooltip.Trigger>
|
||||
{@render children()}
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content>
|
||||
<div class="flex items-center gap-2">
|
||||
<ExternalLinkIcon />
|
||||
<span class="flex items-center gap-1">
|
||||
Opens a new tab in <div class="text-accent">MermaidChart.com</div>
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
{/if}
|
||||
@@ -9,11 +9,10 @@
|
||||
|
||||
<script lang="ts">
|
||||
import MainMenu from '$/components/MainMenu.svelte';
|
||||
import McTooltip from '$/components/MCTooltip.svelte';
|
||||
import McWrapper from '$/components/McWrapper.svelte';
|
||||
import { Button } from '$/components/ui/button';
|
||||
import { Separator } from '$/components/ui/separator';
|
||||
import { Switch } from '$/components/ui/switch';
|
||||
import { env } from '$/util/env';
|
||||
import { dismissPromotion, getActivePromotion } from '$lib/util/promos/promo';
|
||||
import { urlsStore } from '$lib/util/state';
|
||||
import { MCBaseURL } from '$lib/util/util';
|
||||
@@ -84,7 +83,6 @@
|
||||
<nav class="z-50 flex p-6">
|
||||
<div class="flex flex-1 items-center gap-4">
|
||||
<MainMenu />
|
||||
|
||||
<div
|
||||
id="switcher"
|
||||
class="flex items-center justify-center gap-4 font-medium"
|
||||
@@ -95,40 +93,38 @@
|
||||
{/if}
|
||||
Live Editor
|
||||
</a>
|
||||
{#if env.isEnabledMermaidChartLinks}
|
||||
<McTooltip>
|
||||
<div class="hidden items-center justify-center gap-4 md:flex">
|
||||
<Separator orientation="vertical" />
|
||||
<Switch
|
||||
id="editorMode"
|
||||
class="data-[state=checked]:bg-secondary"
|
||||
checked={isReferral}
|
||||
onclick={() => {
|
||||
logEvent('playgroundToggle', { isReferred: isReferral });
|
||||
// Wait for the event to be logged
|
||||
setTimeout(() => {
|
||||
window.open(
|
||||
$urlsStore.mermaidChart.playground,
|
||||
'_self',
|
||||
// Do not send referrer header, if the user already came from playground
|
||||
isReferral ? 'noreferrer' : ''
|
||||
);
|
||||
}, 100);
|
||||
}} />
|
||||
|
||||
<a href={$urlsStore.mermaidChart.playground} class="whitespace-nowrap">
|
||||
Playground <span class="hidden text-sm opacity-50 lg:inline"
|
||||
>- more features, no account required</span>
|
||||
</a>
|
||||
</div>
|
||||
</McTooltip>
|
||||
{/if}
|
||||
<McWrapper>
|
||||
<div class="hidden items-center justify-center gap-4 md:flex">
|
||||
<Separator orientation="vertical" />
|
||||
<Switch
|
||||
id="editorMode"
|
||||
class="data-[state=checked]:bg-secondary"
|
||||
checked={isReferral}
|
||||
onclick={() => {
|
||||
logEvent('playgroundToggle', { isReferred: isReferral });
|
||||
// Wait for the event to be logged
|
||||
setTimeout(() => {
|
||||
window.open(
|
||||
$urlsStore.mermaidChart.playground,
|
||||
'_self',
|
||||
// Do not send referrer header, if the user already came from playground
|
||||
isReferral ? 'noreferrer' : ''
|
||||
);
|
||||
}, 100);
|
||||
}} />
|
||||
|
||||
<a href={$urlsStore.mermaidChart.playground} class="whitespace-nowrap">
|
||||
Playground <span class="hidden text-sm opacity-50 lg:inline"
|
||||
>- more features, no account required</span>
|
||||
</a>
|
||||
</div>
|
||||
</McWrapper>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="menu"
|
||||
class="hidden h-full w-full flex-nowrap items-center justify-between gap-3 overflow-hidden pt-4 text-base md:flex md:w-auto md:items-center md:pt-0">
|
||||
class="hidden flex-nowrap items-center justify-between gap-3 overflow-hidden md:flex">
|
||||
<DropdownNavMenu icon={GithubIcon} links={githubLinks} />
|
||||
<Separator orientation="vertical" />
|
||||
{@render children()}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
let rough: boolean;
|
||||
let view: HTMLDivElement | undefined = $state();
|
||||
let error = $state(false);
|
||||
let panZoom = true;
|
||||
let manualUpdate = true;
|
||||
|
||||
// Set up panZoom state observer to update the store when pan/zoom changes
|
||||
@@ -32,9 +33,7 @@
|
||||
};
|
||||
|
||||
const handlePanZoom = (state: State, graphDiv: SVGSVGElement) => {
|
||||
void Promise.resolve().then(() => {
|
||||
panZoomState.updateElement(graphDiv, state);
|
||||
});
|
||||
panZoomState.updateElement(graphDiv, state);
|
||||
};
|
||||
|
||||
const handleStateChange = async (state: ValidatedState) => {
|
||||
@@ -49,7 +48,12 @@
|
||||
if (container) {
|
||||
manualUpdate = true;
|
||||
// Do not render if there is no change in Code/Config/PanZoom
|
||||
if (code === state.code && config === state.mermaid && rough === state.rough) {
|
||||
if (
|
||||
code === state.code &&
|
||||
config === state.mermaid &&
|
||||
rough === state.rough &&
|
||||
panZoom === state.panZoom
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,6 +64,7 @@
|
||||
code = state.code;
|
||||
config = state.mermaid;
|
||||
rough = state.rough;
|
||||
panZoom = state.panZoom ?? true;
|
||||
const scroll = view?.parentElement?.scrollTop;
|
||||
delete container.dataset.processed;
|
||||
const viewID = uniqueID('graph-');
|
||||
@@ -99,7 +104,9 @@
|
||||
bindFunctions(graphDiv);
|
||||
}
|
||||
}
|
||||
handlePanZoom(state, graphDiv);
|
||||
if (state.panZoom) {
|
||||
handlePanZoom(state, graphDiv);
|
||||
}
|
||||
}
|
||||
if (view?.parentElement && scroll) {
|
||||
view.parentElement.scrollTop = scroll;
|
||||
|
||||
Vendored
+2
@@ -28,6 +28,8 @@ export interface State {
|
||||
updateDiagram: boolean;
|
||||
rough: boolean;
|
||||
// All new options must be optional, as users would have old states saved
|
||||
renderCount?: number;
|
||||
panZoom?: boolean;
|
||||
grid?: boolean;
|
||||
editorMode?: EditorMode;
|
||||
pan?: { x: number; y: number };
|
||||
|
||||
@@ -4,8 +4,8 @@ let shouldSync = true;
|
||||
let updater: () => void;
|
||||
let renderPromise: Promise<void> | undefined;
|
||||
let resolveRenderPromise: (() => void) | undefined;
|
||||
const renderDelay = 1000;
|
||||
const slowRenderThreshold = 150;
|
||||
const renderDelay = 2000;
|
||||
const slowRenderThreshold = 100;
|
||||
|
||||
const debouncedRender = debounce(() => {
|
||||
shouldSync = true;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import DiagramDocButton from '$/components/DiagramDocumentationButton.svelte';
|
||||
import History from '$/components/History/History.svelte';
|
||||
import McTooltip from '$/components/MCTooltip.svelte';
|
||||
import McWrapper from '$/components/McWrapper.svelte';
|
||||
import PanZoomToolbar from '$/components/PanZoomToolbar.svelte';
|
||||
import Share from '$/components/Share.svelte';
|
||||
import SyncRoughToolbar from '$/components/SyncRoughToolbar.svelte';
|
||||
@@ -9,7 +9,6 @@
|
||||
import * as Resizable from '$/components/ui/resizable';
|
||||
import { Toggle } from '$/components/ui/toggle';
|
||||
import VersionSecurityToolbar from '$/components/VersionSecurityToolbar.svelte';
|
||||
import { env } from '$/util/env';
|
||||
import { PanZoomState } from '$/util/panZoom';
|
||||
import Actions from '$lib/components/Actions.svelte';
|
||||
import Card from '$lib/components/Card/Card.svelte';
|
||||
@@ -58,14 +57,12 @@
|
||||
<HistoryIcon />
|
||||
</Toggle>
|
||||
<Share />
|
||||
{#if env.isEnabledMermaidChartLinks}
|
||||
<McTooltip>
|
||||
<Button variant="accent" size="sm" href={$urlsStore.mermaidChart.save} target="_blank">
|
||||
<img class="size-4" src="./mermaidchart-logo.svg" alt="Mermaid Chart" />
|
||||
Save diagram
|
||||
</Button>
|
||||
</McTooltip>
|
||||
{/if}
|
||||
<McWrapper>
|
||||
<Button variant="accent" size="sm" href={$urlsStore.mermaidChart.save} target="_blank">
|
||||
<img class="size-4" src="./mermaidchart-logo.svg" alt="Mermaid Chart" />
|
||||
Save diagram
|
||||
</Button>
|
||||
</McWrapper>
|
||||
</Navbar>
|
||||
|
||||
<div class="flex flex-1 overflow-hidden">
|
||||
|
||||
Reference in New Issue
Block a user