Merge pull request #1939 from mermaid-js/sidv/EnhancedEditButton

chore(MC-4489): Add enhanced edit button
This commit is contained in:
Sidharth Vinod
2026-04-23 14:16:34 +05:30
committed by GitHub
3 changed files with 124 additions and 36 deletions
@@ -0,0 +1,119 @@
<script lang="ts">
import McWrapper from '$/components/McWrapper.svelte';
import MermaidChartIcon from '$/components/MermaidChartIcon.svelte';
import { Button } from '$/components/ui/button';
import { standardizeDiagramType } from '$/util/mermaid';
import { stateStore, urlsStore } from '$/util/state';
import { logMermaidChartClick } from '$/util/stats';
import { quintInOut } from 'svelte/easing';
import { slide } from 'svelte/transition';
const visualEditDiagramTypes = new Set([
'flowchart',
'stateDiagram',
'classDiagram',
'sequenceDiagram',
'er',
'requirement',
'mindmap'
]);
const diagramType = $derived.by(() => {
const dt = $stateStore.diagramType;
return dt ? standardizeDiagramType(dt) : undefined;
});
const showVisualEdit = $derived.by(() => {
return diagramType ? visualEditDiagramTypes.has(diagramType) : false;
});
interface EnhancedEditAction {
campaign: string;
label: string;
medium: 'ai_edit' | 'visual_edit' | 'voice_edit';
source: string;
}
const cycleIntervalMs = 30_000;
let currentActionIndex = $state(0);
const availableActions = $derived.by<EnhancedEditAction[]>(() => {
if (!$stateStore.diagramType) {
return [];
}
const actions: EnhancedEditAction[] = [
{ campaign: 'voice_1', label: 'with voice', medium: 'voice_edit', source: 'voiceEdit' },
{ campaign: 'ai_1', label: 'with AI', medium: 'ai_edit', source: 'aiEdit' }
];
if (showVisualEdit) {
actions.unshift({
campaign: 'visual_1',
label: 'visually',
medium: 'visual_edit',
source: 'visualEdit'
});
}
return actions;
});
const currentAction = $derived.by(() => {
const actions = availableActions;
if (actions.length === 0) {
return undefined;
}
return actions[currentActionIndex % actions.length];
});
$effect(() => {
const actionCount = availableActions.length;
if (actionCount === 0) {
currentActionIndex = 0;
return;
}
if (currentActionIndex >= actionCount) {
currentActionIndex = 0;
}
if (actionCount <= 1) {
return;
}
const intervalID = setInterval(() => {
currentActionIndex = (currentActionIndex + 1) % actionCount;
}, cycleIntervalMs);
return () => clearInterval(intervalID);
});
</script>
{#if currentAction}
<McWrapper>
<Button
variant="secondary"
size="sm"
href={$urlsStore.mermaidChart({
medium: currentAction.medium,
campaign: currentAction.campaign
}).save}
target="_blank"
onclick={() => logMermaidChartClick(currentAction.source)}>
<MermaidChartIcon />
Edit
{#key currentAction.label}
<span
class="-ml-1"
in:slide={{ axis: 'x', easing: quintInOut, delay: 400 }}
out:slide={{ axis: 'x', easing: quintInOut }}>
{currentAction.label}
</span>
{/key}
</Button>
</McWrapper>
{/if}
+3 -1
View File
@@ -141,12 +141,14 @@ export const urlsStore = derived([stateStore], ([{ code, serialized }]) => {
campaign
}: {
medium:
| 'ai_edit'
| 'ai_repair'
| 'main_menu'
| 'save_diagram'
| 'share'
| 'vibe_diagramming'
| 'visual_edit';
| 'visual_edit'
| 'voice_edit';
campaign?: string;
}) => {
const utmSource = getUTMSource();
+2 -35
View File
@@ -3,6 +3,7 @@
import Card from '$/components/Card/Card.svelte';
import DiagramDocButton from '$/components/DiagramDocumentationButton.svelte';
import Editor from '$/components/Editor.svelte';
import EnhancedEditsButton from '$/components/EnhancedEditsButton.svelte';
import History from '$/components/History/History.svelte';
import McWrapper from '$/components/McWrapper.svelte';
import MermaidChartIcon from '$/components/MermaidChartIcon.svelte';
@@ -19,15 +20,12 @@
import VersionSecurityToolbar from '$/components/VersionSecurityToolbar.svelte';
import View from '$/components/View.svelte';
import type { EditorMode, Tab } from '$/types';
import { standardizeDiagramType } from '$/util/mermaid';
import { shouldShowEditorChooser } from '$/util/migration/domainMigration';
import { PanZoomState } from '$/util/panZoom';
import { stateStore, updateCodeStore, urlsStore } from '$/util/state';
import { logEvent, logMermaidChartClick } from '$/util/stats';
import { initHandler } from '$/util/util';
import { onMount } from 'svelte';
import { quintInOut } from 'svelte/easing';
import { slide } from 'svelte/transition';
import CodeIcon from '~icons/custom/code';
import HistoryIcon from '~icons/material-symbols/history';
import GearIcon from '~icons/material-symbols/settings-outline-rounded';
@@ -65,20 +63,6 @@
});
});
const visualEditDiagramTypes = new Set([
'flowchart',
'stateDiagram',
'classDiagram',
'sequenceDiagram',
'er',
'requirement',
'mindmap'
]);
const showVisualEdit = $derived.by(() => {
const dt = $stateStore.diagramType;
return dt ? visualEditDiagramTypes.has(standardizeDiagramType(dt)) : false;
});
let isHistoryOpen = $state(false);
let editorPane: Resizable.Pane | undefined;
@@ -153,24 +137,7 @@
<Resizable.Handle class="mr-1 hidden opacity-0 sm:block" />
<Resizable.Pane minSize={15} class="relative flex h-full flex-1 flex-col overflow-hidden">
<View {panZoomState} shouldShowGrid={$stateStore.grid} />
{#if showVisualEdit}
<div
class="absolute top-0 left-5 hidden md:block"
transition:slide={{ axis: 'x', easing: quintInOut }}>
<McWrapper>
<Button
variant="secondary"
size="sm"
href={$urlsStore.mermaidChart({ medium: 'visual_edit', campaign: 'visual_1' })
.save}
target="_blank"
onclick={() => logMermaidChartClick('visualEdit')}>
<MermaidChartIcon />
Edit Visually
</Button>
</McWrapper>
</div>
{/if}
<div class="absolute top-0 left-5 hidden md:block"><EnhancedEditsButton /></div>
<div class="absolute top-0 right-0"><PanZoomToolbar {panZoomState} /></div>
<div class="absolute right-0 bottom-0"><VersionSecurityToolbar /></div>
<div class="absolute bottom-0 left-0 sm:left-5"><SyncRoughToolbar /></div>