Replace the writable+derived store pipeline in state.ts with a state.svelte.ts module: a deep $state inputState mutated only through the exported update functions, each of which persists the snapshot and re-validates it asynchronously into validatedState.current (with urls.current derived from it). Store subscriptions in components become $effect blocks, and the rough/grid toggles use function bindings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
126 lines
3.4 KiB
TypeScript
126 lines
3.4 KiB
TypeScript
import { C } from '$/constants';
|
|
import { env } from './env';
|
|
import { loadDataFromUrl } from './fileLoaders/loader';
|
|
import { initLoading } from './loading';
|
|
import { isOnMermaidAI } from './migration/domainMigration';
|
|
import { applyMigrations } from './migrations';
|
|
import { initURLSubscription, loadState, updateCodeStore, verifyState } from './state.svelte';
|
|
import { getAnalyticsSafeUrl, initAnalytics, plausible } from './stats';
|
|
|
|
export const getDomain = (url?: string): string => {
|
|
if (!url) return '';
|
|
const domain = new URL(url).hostname;
|
|
return domain;
|
|
};
|
|
|
|
export const loadStateFromURL = (): void => {
|
|
loadState(window.location.hash.slice(1));
|
|
};
|
|
|
|
export const syncDiagram = (): void => {
|
|
updateCodeStore({
|
|
updateDiagram: true
|
|
});
|
|
};
|
|
|
|
export const initHandler = async (): Promise<void> => {
|
|
applyMigrations();
|
|
loadStateFromURL();
|
|
await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error));
|
|
syncDiagram();
|
|
initURLSubscription();
|
|
await initAnalytics();
|
|
plausible?.trackPageview({
|
|
url: getAnalyticsSafeUrl()
|
|
});
|
|
verifyState();
|
|
};
|
|
|
|
export const isMac = navigator.platform.toUpperCase().includes('MAC');
|
|
export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
|
|
export const MCBaseURL = env.isEnabledMermaidChartLinks
|
|
? 'https://mermaid.ai' // 'http://localhost:5174'
|
|
: 'https://example.com';
|
|
|
|
const buildUtmParams = ({
|
|
utmCampaign,
|
|
utmMedium
|
|
}: {
|
|
utmCampaign: string;
|
|
utmMedium: string;
|
|
}): URLSearchParams =>
|
|
new URLSearchParams({
|
|
utm_campaign: utmCampaign,
|
|
utm_medium: utmMedium,
|
|
utm_source: getUTMSource()
|
|
});
|
|
|
|
export const getCheckoutUrl = (utm: { utmCampaign: string; utmMedium: string }): string => {
|
|
const params = buildUtmParams(utm);
|
|
params.set('coupon', 'arDfyFT8');
|
|
params.set('tier', 'plus');
|
|
return `${MCBaseURL}/app/user/billing/checkout?${params.toString()}`;
|
|
};
|
|
|
|
export const getMermaidAiLiveUrl = (utm: { utmCampaign: string; utmMedium: string }): string => {
|
|
return `${MCBaseURL}/live?${buildUtmParams(utm).toString()}`;
|
|
};
|
|
|
|
let count = 0;
|
|
export const errorDebug = (limit = 1000) => {
|
|
count += 1;
|
|
if (count > limit) {
|
|
console.log(count, limit);
|
|
// eslint-disable-next-line no-debugger
|
|
debugger;
|
|
}
|
|
};
|
|
|
|
export const formatJSON = (data: unknown): string => JSON.stringify(data, undefined, 2);
|
|
export const fetchJSON = async <T>(url: string): Promise<T> => {
|
|
const res = await fetch(url);
|
|
return res.json() as T;
|
|
};
|
|
export const fetchText = async (url: string): Promise<string> => {
|
|
const res = await fetch(url);
|
|
return res.text();
|
|
};
|
|
|
|
export const copyToClipboard = async (text: string) => {
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
} catch {
|
|
fallbackCopyToClipboard(text);
|
|
}
|
|
};
|
|
|
|
function fallbackCopyToClipboard(text: string) {
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = text;
|
|
// Make the textarea out of viewport
|
|
textArea.style.position = 'fixed';
|
|
textArea.style.left = '-999999px';
|
|
textArea.style.top = '-999999px';
|
|
document.body.append(textArea);
|
|
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
try {
|
|
// The deprecated but widely supported method
|
|
document.execCommand('copy');
|
|
} catch (error) {
|
|
console.error('Failed to copy:', error);
|
|
throw error;
|
|
} finally {
|
|
textArea.remove();
|
|
}
|
|
}
|
|
|
|
export const getUTMSource = (): string => {
|
|
if (typeof window !== 'undefined' && isOnMermaidAI()) {
|
|
return C.aiLiveEditor;
|
|
}
|
|
return C.utmSource;
|
|
};
|