Merge pull request #1909 from mermaid-js/sidv/correctPrivacyPolicy
feat: Configure Privacy Policy
This commit is contained in:
@@ -19,6 +19,8 @@ ARG MERMAID_KROKI_RENDERER_URL
|
||||
ARG MERMAID_ANALYTICS_URL
|
||||
ARG MERMAID_DOMAIN
|
||||
ARG MERMAID_IS_ENABLED_MERMAID_CHART_LINKS
|
||||
ARG MERMAID_PRIVACY_POLICY_URL
|
||||
ARG MERMAID_HIDE_PRIVACY_POLICY
|
||||
|
||||
COPY . ./
|
||||
|
||||
|
||||
Vendored
+2
@@ -7,6 +7,8 @@ interface ImportMetaEnv {
|
||||
readonly MERMAID_DOCS_URL?: string;
|
||||
readonly MERMAID_DOMAIN?: string;
|
||||
readonly MERMAID_IS_ENABLED_MERMAID_CHART_LINKS?: string;
|
||||
readonly MERMAID_PRIVACY_POLICY_URL?: string;
|
||||
readonly MERMAID_HIDE_PRIVACY_POLICY?: string;
|
||||
// more env variables...
|
||||
}
|
||||
|
||||
|
||||
@@ -1,45 +1,66 @@
|
||||
<script>
|
||||
import ExternalLinkWrapper from '$/components/ExternalLinkWrapper.svelte';
|
||||
import * as Dialog from '$/components/ui/dialog';
|
||||
import { env } from '$/util/env';
|
||||
import { isOnMermaidLive } from '$/util/migration/domainMigration';
|
||||
import ShieldIcon from '~icons/material-symbols/shield-lock-outline-rounded';
|
||||
</script>
|
||||
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger>
|
||||
{#if env.privacyPolicyUrl}
|
||||
<a href={env.privacyPolicyUrl} target="_blank">
|
||||
<ShieldIcon />
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Content class="max-h-full overflow-hidden overflow-y-auto p-12">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title class="flex items-center gap-2 text-xl">
|
||||
<ShieldIcon class="size-8 text-green-700" />
|
||||
Data security
|
||||
</Dialog.Title>
|
||||
</Dialog.Header>
|
||||
</a>
|
||||
{:else}
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger>
|
||||
<ShieldIcon />
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Content class="max-h-full overflow-hidden overflow-y-auto p-12">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title class="flex items-center gap-2 text-xl">
|
||||
<ShieldIcon class="size-8 text-green-700" />
|
||||
Data security
|
||||
</Dialog.Title>
|
||||
</Dialog.Header>
|
||||
|
||||
<p class="text-xl font-semibold">Your diagrams never leave your browser.</p>
|
||||
<p>They're only stored in the URL and your browser's local storage.</p>
|
||||
<p>
|
||||
This is a fully open source, client-side app deployed on <a
|
||||
href="https://github.com/mermaid-js/mermaid-live-editor/deployments"
|
||||
class="underline"
|
||||
target="_blank">GitHub Pages</a>
|
||||
that works offline as a
|
||||
<a href="https://web.dev/explore/progressive-web-apps" target="_blank">Progressive Web App</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
We use self hosted, privacy-friendly Plausible Analytics to collect anonymous usage metadata
|
||||
(diagram types, feature usage, etc.). All data is <a
|
||||
href="https://p.mermaid.live/mermaid.live"
|
||||
class="underline"
|
||||
target="_blank">publicly available</a
|
||||
>.
|
||||
</p>
|
||||
<ExternalLinkWrapper domain="example.com" isVisible>
|
||||
<p class="text-left">
|
||||
External services (PNG/SVG/Kroki exports, "Save to Mermaid Chart", "Repair with AI", etc)
|
||||
will share your diagram with those 3rd parties, and are highlighted in the UI on hover.
|
||||
</p>
|
||||
</ExternalLinkWrapper>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
{#if isOnMermaidLive()}
|
||||
<p class="text-xl font-semibold">Your diagrams never leave your browser.</p>
|
||||
<p>They're only stored in the URL and your browser's local storage.</p>
|
||||
<p>
|
||||
This is a fully open source, client-side app deployed on <a
|
||||
href="https://github.com/mermaid-js/mermaid-live-editor/deployments"
|
||||
class="underline"
|
||||
target="_blank">GitHub Pages</a>
|
||||
that works offline as a
|
||||
<a href="https://web.dev/explore/progressive-web-apps" target="_blank"
|
||||
>Progressive Web App</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
We use self hosted, privacy-friendly Plausible Analytics to collect anonymous usage
|
||||
metadata (diagram types, feature usage, etc.). All data is <a
|
||||
href="https://p.mermaid.live/mermaid.live"
|
||||
class="underline"
|
||||
target="_blank">publicly available</a
|
||||
>.
|
||||
</p>
|
||||
<ExternalLinkWrapper domain="example.com" isVisible>
|
||||
<p class="text-left">
|
||||
External services (PNG/SVG/Kroki exports, "Save to Mermaid Chart", "Repair with AI",
|
||||
etc) will share your diagram with those 3rd parties, and are highlighted in the UI on
|
||||
hover.
|
||||
</p>
|
||||
</ExternalLinkWrapper>
|
||||
{:else}
|
||||
<p>No privacy policy has been configured for this deployment.</p>
|
||||
<p>
|
||||
If you are self-hosting the Mermaid Live Editor, set the
|
||||
<code class="rounded bg-muted px-1.5 py-0.5 text-sm">MERMAID_PRIVACY_POLICY_URL</code>
|
||||
environment variable at build time to link to your privacy policy, or set
|
||||
<code class="rounded bg-muted px-1.5 py-0.5 text-sm">MERMAID_HIDE_PRIVACY_POLICY</code>
|
||||
to <code class="rounded bg-muted px-1.5 py-0.5 text-sm">true</code> to hide this button.
|
||||
</p>
|
||||
{/if}
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
{/if}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Button } from '$/components/ui/button';
|
||||
import { Separator } from '$/components/ui/separator';
|
||||
import { TID } from '$/constants';
|
||||
import { env } from '$/util/env';
|
||||
import { version } from 'mermaid/package.json';
|
||||
import { mode, setMode } from 'mode-watcher';
|
||||
import ThemeIcon from './ThemeIcon.svelte';
|
||||
@@ -11,11 +12,13 @@
|
||||
|
||||
<FloatingToolbar>
|
||||
<span class="text-sm font-semibold opacity-60">v{version}</span>
|
||||
<Button variant="ghost" size="icon" title="Privacy & Security">
|
||||
<Privacy />
|
||||
</Button>
|
||||
{#if !env.hidePrivacyPolicy}
|
||||
<Button variant="ghost" size="icon" title="Privacy & Security">
|
||||
<Privacy />
|
||||
</Button>
|
||||
|
||||
<Separator orientation="vertical" />
|
||||
<Separator orientation="vertical" />
|
||||
{/if}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { logEvent, logMermaidChartClick } from '$/util/stats';
|
||||
import { getCheckoutUrl } from '$/util/util';
|
||||
import CodeIcon from '~icons/custom/code';
|
||||
import ArrowIcon from '~icons/material-symbols/arrow-forward-rounded';
|
||||
import OpenSourceIcon from '~icons/material-symbols/book-2-outline-rounded';
|
||||
import ChatIcon from '~icons/material-symbols/chat-outline-rounded';
|
||||
import EditIcon from '~icons/material-symbols/edit-outline-rounded';
|
||||
@@ -39,6 +40,12 @@
|
||||
logEvent('chooseEditor', { choice: 'openSource' });
|
||||
close();
|
||||
};
|
||||
|
||||
const handleContinueToNewHome = () => {
|
||||
logEvent('chooseEditor', { choice: 'newHome' });
|
||||
close();
|
||||
window.open('https://mermaid.ai/live', '_blank');
|
||||
};
|
||||
</script>
|
||||
|
||||
<Dialog.Root
|
||||
@@ -46,15 +53,15 @@
|
||||
onOpenChange={(v) => {
|
||||
if (!v) handleStartFree();
|
||||
}}>
|
||||
<Dialog.Content class="max-w-2xl bg-pink-50 p-0 dark:bg-background">
|
||||
<Dialog.Header class="px-8 pt-8 pb-0">
|
||||
<Dialog.Content class="flex max-w-2xl flex-col gap-6 bg-pink-50 p-6 dark:bg-background">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title class="text-center text-2xl font-semibold">Choose your editor</Dialog.Title>
|
||||
<Dialog.Description class="-mt-2 text-center text-sm font-light">
|
||||
<Dialog.Description class="text-center text-sm font-light">
|
||||
You'll never see this again
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
|
||||
<div class="grid gap-4 px-6 pt-4 pb-8 sm:grid-cols-2">
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<!-- Mermaid Plus Card -->
|
||||
<div
|
||||
class="relative flex flex-col overflow-hidden rounded-xl border-2 border-accent bg-white shadow dark:bg-card">
|
||||
@@ -62,20 +69,24 @@
|
||||
Recommended
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col p-6">
|
||||
<h3 class="text-xl font-bold">Mermaid Plus</h3>
|
||||
<p class="mb-4 text-sm text-muted-foreground">Unlock AI, storage and collaboration</p>
|
||||
|
||||
<div class="mb-2 flex justify-center">
|
||||
<span
|
||||
class="rounded-full bg-pink-100 px-3 py-0.5 text-xs font-semibold text-pink-700 dark:bg-pink-950 dark:text-pink-300">
|
||||
10% off with code JS26
|
||||
</span>
|
||||
<div class="flex flex-col gap-4 p-6">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold">Mermaid Plus</h3>
|
||||
<p class="text-sm text-muted-foreground">Unlock AI, storage and collaboration</p>
|
||||
</div>
|
||||
|
||||
<Button variant="accent" class="mb-6 w-full" onclick={handleStartTrial}>
|
||||
Start free trial
|
||||
</Button>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex justify-center">
|
||||
<span
|
||||
class="rounded-full bg-pink-100 px-3 py-0.5 text-xs font-semibold text-pink-700 dark:bg-pink-950 dark:text-pink-300">
|
||||
10% off with code JS26
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Button variant="accent" class="w-full" onclick={handleStartTrial}>
|
||||
Start free trial
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ul class="space-y-3 text-sm">
|
||||
<li class="flex items-center gap-2">
|
||||
@@ -103,13 +114,22 @@
|
||||
</div>
|
||||
|
||||
<!-- Open Source Card -->
|
||||
<div class="flex flex-col rounded-xl border bg-white p-6 shadow dark:bg-card">
|
||||
<h3 class="mt-10 text-xl font-bold">Open Source</h3>
|
||||
<p class="mb-4 text-sm text-muted-foreground">Code only, no login, always free</p>
|
||||
<div class="flex flex-col gap-4 rounded-xl border bg-white p-6 shadow dark:bg-card">
|
||||
<div class="flex flex-col justify-end pt-10">
|
||||
<h3 class="text-xl font-bold">Open Source</h3>
|
||||
<p class="text-sm text-muted-foreground">Code only, no login, always free</p>
|
||||
</div>
|
||||
|
||||
<Button variant="outline" class="mb-6 w-full border-accent" onclick={handleStartFree}>
|
||||
Start free
|
||||
</Button>
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="mt-2 text-sm text-muted-foreground">Mermaid has a new home</p>
|
||||
<Button variant="outline" class="w-full border-accent" onclick={handleContinueToNewHome}>
|
||||
Continue to mermaid.ai/live
|
||||
<ArrowIcon class="ml-1 size-4" />
|
||||
</Button>
|
||||
<Button variant="outline" class="w-full" onclick={handleStartFree}>
|
||||
Stay on mermaid.live
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ul class="space-y-3 text-sm">
|
||||
<li class="flex items-center gap-2">
|
||||
@@ -131,5 +151,14 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<a
|
||||
href="https://mermaid.ai/privacy-policy"
|
||||
target="_blank"
|
||||
class="text-sm text-foreground underline hover:text-accent">
|
||||
mermaid.ai Privacy Policy
|
||||
</a>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
@@ -2,7 +2,9 @@ export const env = {
|
||||
analyticsUrl: import.meta.env.MERMAID_ANALYTICS_URL ?? '',
|
||||
docsUrl: import.meta.env.MERMAID_DOCS_URL ?? 'https://mermaid.js.org',
|
||||
domain: import.meta.env.MERMAID_DOMAIN ?? '',
|
||||
hidePrivacyPolicy: import.meta.env.MERMAID_HIDE_PRIVACY_POLICY === 'true',
|
||||
isEnabledMermaidChartLinks: import.meta.env.MERMAID_IS_ENABLED_MERMAID_CHART_LINKS === 'true',
|
||||
krokiRendererUrl: import.meta.env.MERMAID_KROKI_RENDERER_URL ?? '',
|
||||
privacyPolicyUrl: import.meta.env.MERMAID_PRIVACY_POLICY_URL ?? '',
|
||||
rendererUrl: import.meta.env.MERMAID_RENDERER_URL ?? ''
|
||||
} as const;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { C } from '$/constants';
|
||||
import { env } from '$/util/env';
|
||||
|
||||
const mermaidAiDomain = 'mermaid.ai';
|
||||
const mermaidLiveDomain = 'mermaid.live';
|
||||
|
||||
/**
|
||||
* Check if we're on mermaid.ai
|
||||
@@ -11,6 +12,14 @@ export const isOnMermaidAI = (): boolean => {
|
||||
return domain === mermaidAiDomain || domain.endsWith(`.${mermaidAiDomain}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if we're on mermaid.live
|
||||
*/
|
||||
export const isOnMermaidLive = (): boolean => {
|
||||
const domain = window.location.hostname;
|
||||
return domain === mermaidLiveDomain || domain.endsWith(`.${mermaidLiveDomain}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the current URL has pako data (diagram content from a shared link).
|
||||
*/
|
||||
@@ -47,6 +56,7 @@ const isReferredFromMermaid = (): boolean => {
|
||||
*/
|
||||
export const shouldShowEditorChooser = (): boolean => {
|
||||
if (!env.isEnabledMermaidChartLinks) return false;
|
||||
if (!isOnMermaidAI() && !isOnMermaidLive()) return false;
|
||||
if (window.innerWidth < 640) return false;
|
||||
if (window.localStorage.getItem(C.editorChooserDismissedKey) === 'true') return false;
|
||||
if (hasPakoData()) return false;
|
||||
|
||||
Reference in New Issue
Block a user