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} + +{/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