diff --git a/src/lib/components/AIPromptPopup.svelte b/src/lib/components/AIPromptPopup.svelte
new file mode 100644
index 00000000..ded983f1
--- /dev/null
+++ b/src/lib/components/AIPromptPopup.svelte
@@ -0,0 +1,174 @@
+
+
+
+
+{#if show}
+
+
+
+
+
+
+
+ Sign Up at Mermaid.ai to try AI
+
+
+
+{/if}
+
+
diff --git a/src/lib/components/DesktopEditor.svelte b/src/lib/components/DesktopEditor.svelte
index cb54ac5b..ea7b4868 100644
--- a/src/lib/components/DesktopEditor.svelte
+++ b/src/lib/components/DesktopEditor.svelte
@@ -1,7 +1,8 @@
-
+
+
+
+
aiPromptManager.updateHeight(height)}
+ onClose={closePopup}
+ onTryFree={() => {
+ window.open($urlsStore.mermaidChart({ medium: 'vibe_diagramming' }).save, '_blank');
+ closePopup();
+ }} />
+
+
+
+
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 2e71e336..9a83ddba 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -10,5 +10,6 @@ export const TID = {
} as const;
export const C = {
+ aiLiveEditor: 'ai_live_editor',
utmSource: 'mermaid_live_editor'
} as const;
diff --git a/src/lib/util/AIPromptViewZoneManager.ts b/src/lib/util/AIPromptViewZoneManager.ts
new file mode 100644
index 00000000..483363eb
--- /dev/null
+++ b/src/lib/util/AIPromptViewZoneManager.ts
@@ -0,0 +1,71 @@
+import type * as monaco from 'monaco-editor';
+
+export class AIPromptViewZoneManager {
+ private editor: monaco.editor.IStandaloneCodeEditor | null = null;
+ private viewZoneId: string | null = null;
+ private viewZoneNode: HTMLElement | null = null;
+ private viewZone: monaco.editor.IViewZone | null = null;
+ private topPopupSpace = 8;
+
+ public setEditor(editor: monaco.editor.IStandaloneCodeEditor): void {
+ this.editor = editor;
+ }
+
+ public show(lineNumber: number, node: HTMLElement, height: number): void {
+ if (!this.editor) return;
+
+ this.viewZoneNode = node;
+
+ this.editor.changeViewZones((changeAccessor) => {
+ if (this.viewZoneId) {
+ changeAccessor.removeZone(this.viewZoneId);
+ }
+
+ const domNode = document.createElement('div');
+ domNode.style.top = '0px';
+ domNode.style.paddingTop = '8px';
+ domNode.style.paddingBottom = '0px';
+ domNode.appendChild(this.viewZoneNode as Node);
+
+ this.viewZone = {
+ afterLineNumber: lineNumber,
+ heightInPx: height + this.topPopupSpace,
+ domNode: domNode
+ };
+
+ this.viewZoneId = changeAccessor.addZone(this.viewZone);
+ });
+ }
+
+ public updateHeight(height: number): void {
+ if (!this.editor || !this.viewZoneId || !this.viewZone) return;
+
+ this.viewZone.heightInPx = height + this.topPopupSpace;
+ this.editor.changeViewZones((changeAccessor) => {
+ if (this.viewZoneId) {
+ changeAccessor.layoutZone(this.viewZoneId);
+ }
+ });
+ }
+
+ public hide(): void {
+ if (!this.editor || !this.viewZoneId) return;
+
+ this.editor.changeViewZones((changeAccessor) => {
+ if (this.viewZoneId) {
+ changeAccessor.removeZone(this.viewZoneId);
+ this.viewZoneId = null;
+ this.viewZone = null;
+ }
+ });
+
+ if (this.viewZoneNode) {
+ this.viewZoneNode = null;
+ }
+ }
+
+ public destroy(): void {
+ this.hide();
+ this.editor = null;
+ }
+}
diff --git a/src/lib/util/state.ts b/src/lib/util/state.ts
index 06ffb259..b52cd78a 100644
--- a/src/lib/util/state.ts
+++ b/src/lib/util/state.ts
@@ -1,4 +1,3 @@
-import { C } from '$/constants';
import type { ErrorHash, MarkerData, State, ValidatedState } from '$/types';
import { debounce } from 'lodash-es';
import type { MermaidConfig } from 'mermaid';
@@ -12,7 +11,7 @@ import {
import { parse } from './mermaid';
import { localStorage, persist } from './persist';
import { deserializeState, pakoSerde, serializeState } from './serde';
-import { errorDebug, formatJSON, MCBaseURL } from './util';
+import { errorDebug, formatJSON, getUTMSource, MCBaseURL } from './util';
export const defaultState: State = {
code: `flowchart TD
@@ -140,10 +139,11 @@ export const urlsStore = derived([stateStore], ([{ code, serialized }]) => {
mermaidChart: ({
medium
}: {
- medium: 'ai_repair' | 'main_menu' | 'save_diagram' | 'share';
+ medium: 'ai_repair' | 'main_menu' | 'save_diagram' | 'share' | 'vibe_diagramming';
}) => {
+ const utmSource = getUTMSource();
const params = new URLSearchParams({
- utm_source: C.utmSource,
+ utm_source: utmSource,
utm_medium: medium
}).toString();
return {
diff --git a/src/lib/util/util.ts b/src/lib/util/util.ts
index 08552c55..8eb5e2be 100644
--- a/src/lib/util/util.ts
+++ b/src/lib/util/util.ts
@@ -1,3 +1,4 @@
+import { C } from '$/constants';
import { env } from './env';
import { loadDataFromUrl } from './fileLoaders/loader';
import { initLoading } from './loading';
@@ -88,3 +89,10 @@ function fallbackCopyToClipboard(text: string) {
textArea.remove();
}
}
+
+export const getUTMSource = (): string => {
+ if (typeof window !== 'undefined' && window.location.host.includes('mermaid.ai')) {
+ return C.aiLiveEditor;
+ }
+ return C.utmSource;
+};
diff --git a/static/icons/use-chat-dark.svg b/static/icons/use-chat-dark.svg
new file mode 100644
index 00000000..b24e7143
--- /dev/null
+++ b/static/icons/use-chat-dark.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/static/icons/use-chat.svg b/static/icons/use-chat.svg
new file mode 100644
index 00000000..5f4d7e45
--- /dev/null
+++ b/static/icons/use-chat.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file