mirror of
https://github.com/Zie619/n8n-workflows.git
synced 2026-10-30 18:03:42 +08:00
feat: Add interactive workflow canvas preview
- Add n8n-demo web component for visual workflow preview - Split modal layout with canvas always visible on right side - Add JSON viewer with copy button - Support drag to pan and scroll to zoom - Add theme support (light/dark) - Mobile responsive design
This commit is contained in:
+325
-165
@@ -6,8 +6,9 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>⚡ N8N Workflow Documentation</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
||||
<!-- n8n Demo Web Component for interactive workflow canvas preview -->
|
||||
<script type="module" src="https://n8n-io.github.io/n8n-demo-webcomponent/n8n-demo.js"></script>
|
||||
<!-- Web Components polyfill + n8n Demo Web Component for interactive workflow canvas preview -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.8.0/webcomponents-loader.min.js"></script>
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@n8n_io/n8n-demo-component@latest/n8n-demo.bundled.js"></script>
|
||||
<style>
|
||||
/* Modern CSS Reset and Base */
|
||||
* {
|
||||
@@ -434,19 +435,22 @@
|
||||
.modal-content {
|
||||
background: var(--bg);
|
||||
border-radius: 0.75rem;
|
||||
max-width: 800px;
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 1.5rem;
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@@ -464,7 +468,189 @@
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-left {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
overflow-y: auto;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.modal-right {
|
||||
width: 50%;
|
||||
min-width: 400px;
|
||||
border-left: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.modal-right-header {
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.modal-right-header h4 {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-right-footer {
|
||||
padding: 1rem 1.5rem;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.modal-right-footer .action-btn {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.action-btn.ai-btn {
|
||||
background: linear-gradient(135deg, #8b5cf6, #6366f1);
|
||||
color: white;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.action-btn.ai-btn:hover {
|
||||
background: linear-gradient(135deg, #7c3aed, #4f46e5);
|
||||
}
|
||||
|
||||
.action-btn.secondary {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text);
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
.action-btn.secondary:hover {
|
||||
background: var(--bg-secondary);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.action-btn.secondary.active {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* JSON Panel */
|
||||
.json-panel {
|
||||
border-top: 1px solid var(--border);
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.json-panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--bg-tertiary);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.copy-btn-small {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.7rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.copy-btn-small:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.copy-btn-small.copied {
|
||||
background: var(--success);
|
||||
}
|
||||
|
||||
.json-panel .json-viewer {
|
||||
margin: 0;
|
||||
padding: 0.75rem 1rem;
|
||||
background: var(--bg-secondary);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.7rem;
|
||||
color: var(--text);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.canvas-wrapper {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.canvas-wrapper n8n-demo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.canvas-wrapper .canvas-loading,
|
||||
.canvas-wrapper .canvas-error {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.canvas-wrapper .canvas-error {
|
||||
color: var(--error);
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.modal-body {
|
||||
flex-direction: column;
|
||||
}
|
||||
.modal-right {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
border-left: none;
|
||||
border-top: 1px solid var(--border);
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.workflow-detail {
|
||||
@@ -807,67 +993,45 @@
|
||||
<button class="modal-close" id="modalClose">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="workflow-detail">
|
||||
<h4>Description</h4>
|
||||
<p id="modalDescription">Loading...</p>
|
||||
</div>
|
||||
|
||||
<div class="workflow-detail">
|
||||
<h4>Statistics</h4>
|
||||
<div id="modalStats">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="workflow-detail">
|
||||
<h4>Integrations</h4>
|
||||
<div id="modalIntegrations">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="workflow-detail">
|
||||
<h4>Actions</h4>
|
||||
<div class="workflow-actions">
|
||||
<a id="downloadBtn" class="action-btn primary" href="#" download>📥 Download JSON</a>
|
||||
<button id="viewCanvasBtn" class="action-btn">🎨 View Canvas</button>
|
||||
<button id="viewJsonBtn" class="action-btn">📄 View JSON</button>
|
||||
<button id="viewDiagramBtn" class="action-btn">📊 View Diagram</button>
|
||||
<!-- Left side: Info -->
|
||||
<div class="modal-left">
|
||||
<div class="workflow-detail">
|
||||
<h4>Description</h4>
|
||||
<p id="modalDescription">Loading...</p>
|
||||
</div>
|
||||
|
||||
<div class="workflow-detail">
|
||||
<h4>Statistics</h4>
|
||||
<div id="modalStats">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="workflow-detail">
|
||||
<h4>Integrations</h4>
|
||||
<div id="modalIntegrations">Loading...</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="workflow-detail hidden" id="canvasSection">
|
||||
<div class="section-header">
|
||||
<h4>n8n Canvas Preview</h4>
|
||||
<div class="diagram-controls">
|
||||
<span class="zoom-info">Interactive canvas - drag to pan, scroll to zoom</span>
|
||||
</div>
|
||||
<!-- Right side: Canvas Preview (always visible) -->
|
||||
<div class="modal-right">
|
||||
<div class="modal-right-header">
|
||||
<h4>Workflow Preview</h4>
|
||||
<span class="zoom-info">Drag to pan • Scroll to zoom</span>
|
||||
</div>
|
||||
<div id="canvasContainer" class="canvas-container">
|
||||
<div class="canvas-wrapper">
|
||||
<div id="canvasViewer" class="canvas-loading">Loading canvas preview...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="workflow-detail hidden" id="jsonSection">
|
||||
<div class="section-header">
|
||||
<h4>Workflow JSON</h4>
|
||||
<button id="copyJsonBtn" class="copy-btn" title="Copy JSON to clipboard">
|
||||
📋 Copy
|
||||
</button>
|
||||
<div class="modal-right-footer">
|
||||
<a id="downloadBtn" class="action-btn primary" href="#" download>📥 Download</a>
|
||||
<button id="viewJsonBtn" class="action-btn secondary">📄 JSON</button>
|
||||
<button id="editWithAIBtn" class="action-btn ai-btn">✨ Edit with AI</button>
|
||||
</div>
|
||||
<div class="json-viewer" id="jsonViewer">Loading...</div>
|
||||
</div>
|
||||
|
||||
<div class="workflow-detail hidden" id="diagramSection">
|
||||
<div class="section-header">
|
||||
<h4>Workflow Diagram</h4>
|
||||
<div class="diagram-controls">
|
||||
<button id="zoomInBtn" class="zoom-btn" title="Zoom In">🔍+</button>
|
||||
<button id="zoomOutBtn" class="zoom-btn" title="Zoom Out">🔍-</button>
|
||||
<button id="zoomResetBtn" class="zoom-btn" title="Reset Zoom">🔄</button>
|
||||
<button id="copyDiagramBtn" class="copy-btn" title="Copy diagram code to clipboard">
|
||||
📋 Copy
|
||||
</button>
|
||||
<div class="json-panel hidden" id="jsonSection">
|
||||
<div class="json-panel-header">
|
||||
<span>Workflow JSON</span>
|
||||
<button id="copyJsonBtn" class="copy-btn-small" title="Copy JSON">📋 Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="diagramContainer" class="diagram-container">
|
||||
<div id="diagramViewer">Loading diagram...</div>
|
||||
<pre class="json-viewer" id="jsonViewer">Loading...</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -925,22 +1089,12 @@
|
||||
modalStats: document.getElementById('modalStats'),
|
||||
modalIntegrations: document.getElementById('modalIntegrations'),
|
||||
downloadBtn: document.getElementById('downloadBtn'),
|
||||
viewCanvasBtn: document.getElementById('viewCanvasBtn'),
|
||||
editWithAIBtn: document.getElementById('editWithAIBtn'),
|
||||
viewJsonBtn: document.getElementById('viewJsonBtn'),
|
||||
viewDiagramBtn: document.getElementById('viewDiagramBtn'),
|
||||
canvasSection: document.getElementById('canvasSection'),
|
||||
canvasContainer: document.getElementById('canvasContainer'),
|
||||
canvasViewer: document.getElementById('canvasViewer'),
|
||||
jsonSection: document.getElementById('jsonSection'),
|
||||
jsonViewer: document.getElementById('jsonViewer'),
|
||||
diagramSection: document.getElementById('diagramSection'),
|
||||
diagramViewer: document.getElementById('diagramViewer'),
|
||||
diagramContainer: document.getElementById('diagramContainer'),
|
||||
copyJsonBtn: document.getElementById('copyJsonBtn'),
|
||||
copyDiagramBtn: document.getElementById('copyDiagramBtn'),
|
||||
zoomInBtn: document.getElementById('zoomInBtn'),
|
||||
zoomOutBtn: document.getElementById('zoomOutBtn'),
|
||||
zoomResetBtn: document.getElementById('zoomResetBtn')
|
||||
copyJsonBtn: document.getElementById('copyJsonBtn')
|
||||
};
|
||||
|
||||
this.searchDebounceTimer = null;
|
||||
@@ -1041,38 +1195,18 @@
|
||||
}
|
||||
});
|
||||
|
||||
this.elements.viewCanvasBtn.addEventListener('click', () => {
|
||||
this.toggleCanvasView();
|
||||
});
|
||||
|
||||
this.elements.viewJsonBtn.addEventListener('click', () => {
|
||||
this.toggleJsonView();
|
||||
});
|
||||
|
||||
this.elements.viewDiagramBtn.addEventListener('click', () => {
|
||||
this.toggleDiagramView();
|
||||
});
|
||||
|
||||
// Copy button events
|
||||
this.elements.copyJsonBtn.addEventListener('click', () => {
|
||||
this.copyToClipboard(this.currentJsonData, 'copyJsonBtn');
|
||||
});
|
||||
|
||||
this.elements.copyDiagramBtn.addEventListener('click', () => {
|
||||
this.copyToClipboard(this.currentDiagramData, 'copyDiagramBtn');
|
||||
});
|
||||
|
||||
// Zoom control events
|
||||
this.elements.zoomInBtn.addEventListener('click', () => {
|
||||
this.zoomDiagram(1.2);
|
||||
});
|
||||
|
||||
this.elements.zoomOutBtn.addEventListener('click', () => {
|
||||
this.zoomDiagram(0.8);
|
||||
});
|
||||
|
||||
this.elements.zoomResetBtn.addEventListener('click', () => {
|
||||
this.resetDiagramZoom();
|
||||
// Edit with AI button
|
||||
this.elements.editWithAIBtn.addEventListener('click', () => {
|
||||
this.openEditWithAI();
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
@@ -1080,20 +1214,6 @@
|
||||
if (e.key === 'Escape') {
|
||||
this.closeModal();
|
||||
}
|
||||
|
||||
// Zoom shortcuts when diagram is visible
|
||||
if (!this.elements.diagramSection.classList.contains('hidden')) {
|
||||
if (e.key === '+' || e.key === '=') {
|
||||
e.preventDefault();
|
||||
this.zoomDiagram(1.2);
|
||||
} else if (e.key === '-') {
|
||||
e.preventDefault();
|
||||
this.zoomDiagram(0.8);
|
||||
} else if (e.key === '0' && e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
this.resetDiagramZoom();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1479,103 +1599,143 @@
|
||||
this.elements.downloadBtn.href = `/api/workflows/${workflow.filename}/download`;
|
||||
this.elements.downloadBtn.download = workflow.filename;
|
||||
|
||||
// Set Edit with AI link (FlowEngine import)
|
||||
const workflowUrl = encodeURIComponent(window.location.origin + `/api/workflows/${workflow.filename}/download`);
|
||||
this.elements.editWithAIBtn.href = `https://flowengine.cloud/import?workflow=${workflowUrl}&name=${encodeURIComponent(workflow.name)}`;
|
||||
|
||||
// Reset view states
|
||||
this.elements.canvasSection.classList.add('hidden');
|
||||
this.elements.jsonSection.classList.add('hidden');
|
||||
this.elements.diagramSection.classList.add('hidden');
|
||||
|
||||
this.elements.workflowModal.classList.remove('hidden');
|
||||
|
||||
// Auto-load canvas preview
|
||||
this.loadCanvasPreview();
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
this.elements.workflowModal.classList.add('hidden');
|
||||
this.currentWorkflow = null;
|
||||
this.currentJsonData = null;
|
||||
this.currentDiagramData = null;
|
||||
this.diagramSvg = null;
|
||||
this.diagramZoom = 1;
|
||||
this.diagramPan = { x: 0, y: 0 };
|
||||
this.isDragging = false;
|
||||
|
||||
// Reset button states
|
||||
this.elements.viewCanvasBtn.textContent = '🎨 View Canvas';
|
||||
this.elements.viewJsonBtn.textContent = '📄 View JSON';
|
||||
this.elements.viewDiagramBtn.textContent = '📊 View Diagram';
|
||||
this.elements.viewJsonBtn.textContent = '📄 JSON';
|
||||
this.elements.viewJsonBtn.classList.remove('active');
|
||||
|
||||
// Reset copy button states
|
||||
this.resetCopyButton('copyJsonBtn');
|
||||
this.resetCopyButton('copyDiagramBtn');
|
||||
|
||||
// Clear canvas content
|
||||
this.elements.canvasViewer.innerHTML = '<div class="canvas-loading">Loading canvas preview...</div>';
|
||||
}
|
||||
|
||||
async toggleCanvasView() {
|
||||
// FlowEngine node type mapping for proper icons in canvas preview
|
||||
// Maps FlowEngine custom nodes to official n8n nodes for icon display
|
||||
static PREVIEW_NODE_TYPE_MAP = {
|
||||
'CUSTOM.flowEngineLlm': '@n8n/n8n-nodes-langchain.lmChatOpenAi',
|
||||
'n8n-nodes-flowengine.flowEngine': 'n8n-nodes-base.code',
|
||||
'n8n-nodes-flowengine-session-id.flowEngineSessionId': 'n8n-nodes-base.set',
|
||||
'n8n-nodes-flowengine-data-standardize-clean.dataCleaner': 'n8n-nodes-base.itemLists',
|
||||
};
|
||||
|
||||
// Map FlowEngine nodes to official n8n nodes for proper icon rendering
|
||||
mapNodeTypesForPreview(workflowJson) {
|
||||
if (!workflowJson || !workflowJson.nodes) return workflowJson;
|
||||
|
||||
const mappedWorkflow = JSON.parse(JSON.stringify(workflowJson));
|
||||
|
||||
mappedWorkflow.nodes = mappedWorkflow.nodes.map(node => {
|
||||
const mappedType = WorkflowApp.PREVIEW_NODE_TYPE_MAP[node.type];
|
||||
if (mappedType) {
|
||||
return { ...node, type: mappedType };
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
return mappedWorkflow;
|
||||
}
|
||||
|
||||
async loadCanvasPreview() {
|
||||
if (!this.currentWorkflow) return;
|
||||
|
||||
const isVisible = !this.elements.canvasSection.classList.contains('hidden');
|
||||
try {
|
||||
this.elements.canvasViewer.innerHTML = '<div class="canvas-loading">Loading canvas preview...</div>';
|
||||
|
||||
if (isVisible) {
|
||||
this.elements.canvasSection.classList.add('hidden');
|
||||
this.elements.viewCanvasBtn.textContent = '🎨 View Canvas';
|
||||
} else {
|
||||
try {
|
||||
this.elements.canvasViewer.innerHTML = '<div class="canvas-loading">Loading canvas preview...</div>';
|
||||
this.elements.canvasSection.classList.remove('hidden');
|
||||
this.elements.viewCanvasBtn.textContent = '🎨 Hide Canvas';
|
||||
// Fetch the workflow JSON
|
||||
const data = await this.apiCall(`/workflows/${this.currentWorkflow.filename}`);
|
||||
const rawWorkflowJson = data.raw_json;
|
||||
|
||||
// Fetch the workflow JSON
|
||||
const data = await this.apiCall(`/workflows/${this.currentWorkflow.filename}`);
|
||||
const workflowJson = data.raw_json;
|
||||
// Store raw JSON for Edit with AI and JSON view
|
||||
this.currentJsonData = JSON.stringify(rawWorkflowJson, null, 2);
|
||||
this.rawWorkflowJson = rawWorkflowJson;
|
||||
|
||||
// Detect current theme
|
||||
const currentTheme = document.documentElement.getAttribute('data-theme') || 'light';
|
||||
// Map FlowEngine node types to official n8n nodes for proper icons
|
||||
const workflowJson = this.mapNodeTypesForPreview(rawWorkflowJson);
|
||||
|
||||
// Create the n8n-demo web component
|
||||
const n8nDemo = document.createElement('n8n-demo');
|
||||
n8nDemo.setAttribute('workflow', JSON.stringify(workflowJson));
|
||||
n8nDemo.setAttribute('theme', currentTheme);
|
||||
n8nDemo.setAttribute('frame', 'false');
|
||||
// Detect current theme
|
||||
const currentTheme = document.documentElement.getAttribute('data-theme') || 'light';
|
||||
|
||||
// Clear and add the component
|
||||
this.elements.canvasViewer.innerHTML = '';
|
||||
this.elements.canvasViewer.appendChild(n8nDemo);
|
||||
// Create the n8n-demo web component
|
||||
const n8nDemo = document.createElement('n8n-demo');
|
||||
n8nDemo.setAttribute('workflow', JSON.stringify(workflowJson));
|
||||
n8nDemo.setAttribute('theme', currentTheme);
|
||||
n8nDemo.setAttribute('frame', 'false');
|
||||
n8nDemo.setAttribute('collapseformobile', 'false');
|
||||
n8nDemo.setAttribute('zoomtofit', 'true');
|
||||
|
||||
} catch (error) {
|
||||
this.elements.canvasViewer.innerHTML = `
|
||||
<div class="canvas-error">
|
||||
<div>
|
||||
<div style="font-size: 2rem; margin-bottom: 0.5rem;">⚠️</div>
|
||||
<div>Error loading canvas preview</div>
|
||||
<div style="font-size: 0.875rem; margin-top: 0.5rem; opacity: 0.8;">${this.escapeHtml(error.message)}</div>
|
||||
</div>
|
||||
// Clear and add the component
|
||||
this.elements.canvasViewer.innerHTML = '';
|
||||
this.elements.canvasViewer.appendChild(n8nDemo);
|
||||
|
||||
} catch (error) {
|
||||
this.elements.canvasViewer.innerHTML = `
|
||||
<div class="canvas-error">
|
||||
<div>
|
||||
<div style="font-size: 2rem; margin-bottom: 0.5rem;">⚠️</div>
|
||||
<div>Error loading canvas preview</div>
|
||||
<div style="font-size: 0.875rem; margin-top: 0.5rem; opacity: 0.8;">${this.escapeHtml(error.message)}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
async toggleJsonView() {
|
||||
openEditWithAI() {
|
||||
if (!this.rawWorkflowJson || !this.currentWorkflow) {
|
||||
alert('Please wait for the workflow to load');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Encode workflow JSON as base64
|
||||
const jsonString = JSON.stringify(this.rawWorkflowJson);
|
||||
const base64 = btoa(unescape(encodeURIComponent(jsonString)));
|
||||
const name = encodeURIComponent(this.currentWorkflow.name);
|
||||
|
||||
// Open FlowEngine import page with encoded workflow
|
||||
const url = `https://flowengine.cloud/import?data=${base64}&name=${name}`;
|
||||
window.open(url, '_blank');
|
||||
} catch (error) {
|
||||
console.error('Failed to open Edit with AI:', error);
|
||||
alert('Failed to open Edit with AI. The workflow may be too large.');
|
||||
}
|
||||
}
|
||||
|
||||
toggleJsonView() {
|
||||
if (!this.currentWorkflow) return;
|
||||
|
||||
const isVisible = !this.elements.jsonSection.classList.contains('hidden');
|
||||
|
||||
if (isVisible) {
|
||||
this.elements.jsonSection.classList.add('hidden');
|
||||
this.elements.viewJsonBtn.textContent = '📄 View JSON';
|
||||
this.elements.viewJsonBtn.textContent = '📄 JSON';
|
||||
this.elements.viewJsonBtn.classList.remove('active');
|
||||
} else {
|
||||
try {
|
||||
this.elements.jsonViewer.textContent = 'Loading...';
|
||||
this.elements.jsonSection.classList.remove('hidden');
|
||||
this.elements.viewJsonBtn.textContent = '📄 Hide JSON';
|
||||
|
||||
const data = await this.apiCall(`/workflows/${this.currentWorkflow.filename}`);
|
||||
const jsonString = JSON.stringify(data.raw_json, null, 2);
|
||||
this.currentJsonData = jsonString;
|
||||
this.elements.jsonViewer.textContent = jsonString;
|
||||
} catch (error) {
|
||||
this.elements.jsonViewer.textContent = 'Error loading JSON: ' + error.message;
|
||||
this.currentJsonData = null;
|
||||
this.elements.jsonSection.classList.remove('hidden');
|
||||
this.elements.viewJsonBtn.textContent = '📄 Hide';
|
||||
this.elements.viewJsonBtn.classList.add('active');
|
||||
// JSON is already loaded by loadCanvasPreview
|
||||
if (this.currentJsonData) {
|
||||
this.elements.jsonViewer.textContent = this.currentJsonData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user