mirror of
https://github.com/Zie619/n8n-workflows.git
synced 2025-11-25 03:15:25 +08:00
Compare commits
4 Commits
39e7e46e36
...
f8639776c9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8639776c9 | ||
|
|
67a5bb92c5 | ||
|
|
54688735f7 | ||
|
|
5ce4fd4ae1 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -90,4 +90,7 @@ package-lock.json
|
||||
.python-version
|
||||
|
||||
# Claude Code local settings (created during development)
|
||||
.claude/settings.local.json
|
||||
.claude/settings.local.json
|
||||
|
||||
# E3D development directory
|
||||
.e3d/
|
||||
@@ -529,12 +529,76 @@
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
overflow-x: auto;
|
||||
overflow: visible;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.mermaid svg {
|
||||
max-width: 100%;
|
||||
max-width: none;
|
||||
height: auto;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.diagram-container {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
height: 500px;
|
||||
position: relative;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.diagram-container.dragging {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.diagram-container .mermaid {
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.diagram-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.zoom-btn {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.zoom-btn:hover {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.zoom-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.zoom-info {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@@ -739,11 +803,18 @@
|
||||
<div class="workflow-detail hidden" id="diagramSection">
|
||||
<div class="section-header">
|
||||
<h4>Workflow Diagram</h4>
|
||||
<button id="copyDiagramBtn" class="copy-btn" title="Copy diagram code to clipboard">
|
||||
📋 Copy
|
||||
</button>
|
||||
<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>
|
||||
</div>
|
||||
<div id="diagramContainer" class="diagram-container">
|
||||
<div id="diagramViewer">Loading diagram...</div>
|
||||
</div>
|
||||
<div id="diagramViewer">Loading diagram...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -806,14 +877,23 @@
|
||||
jsonViewer: document.getElementById('jsonViewer'),
|
||||
diagramSection: document.getElementById('diagramSection'),
|
||||
diagramViewer: document.getElementById('diagramViewer'),
|
||||
diagramContainer: document.getElementById('diagramContainer'),
|
||||
copyJsonBtn: document.getElementById('copyJsonBtn'),
|
||||
copyDiagramBtn: document.getElementById('copyDiagramBtn')
|
||||
copyDiagramBtn: document.getElementById('copyDiagramBtn'),
|
||||
zoomInBtn: document.getElementById('zoomInBtn'),
|
||||
zoomOutBtn: document.getElementById('zoomOutBtn'),
|
||||
zoomResetBtn: document.getElementById('zoomResetBtn')
|
||||
};
|
||||
|
||||
this.searchDebounceTimer = null;
|
||||
this.currentWorkflow = null;
|
||||
this.currentJsonData = null;
|
||||
this.currentDiagramData = null;
|
||||
this.diagramZoom = 1;
|
||||
this.diagramSvg = null;
|
||||
this.diagramPan = { x: 0, y: 0 };
|
||||
this.isDragging = false;
|
||||
this.lastMousePos = { x: 0, y: 0 };
|
||||
this.init();
|
||||
}
|
||||
|
||||
@@ -920,11 +1000,38 @@
|
||||
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();
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
document.addEventListener('keydown', (e) => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1322,6 +1429,10 @@
|
||||
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.viewJsonBtn.textContent = '📄 View JSON';
|
||||
@@ -1382,6 +1493,13 @@
|
||||
// Re-initialize Mermaid for the new diagram
|
||||
if (typeof mermaid !== 'undefined') {
|
||||
mermaid.init(undefined, this.elements.diagramViewer.querySelector('.mermaid'));
|
||||
|
||||
// Store reference to SVG and reset zoom
|
||||
setTimeout(() => {
|
||||
this.diagramSvg = this.elements.diagramViewer.querySelector('.mermaid svg');
|
||||
this.resetDiagramZoom();
|
||||
this.setupDiagramPanning();
|
||||
}, 100);
|
||||
}
|
||||
} catch (error) {
|
||||
this.elements.diagramViewer.textContent = 'Error loading diagram: ' + error.message;
|
||||
@@ -1390,7 +1508,109 @@
|
||||
}
|
||||
}
|
||||
|
||||
updateLoadMoreButton() {
|
||||
zoomDiagram(factor) {
|
||||
if (!this.diagramSvg) return;
|
||||
|
||||
this.diagramZoom *= factor;
|
||||
this.diagramZoom = Math.max(0.1, Math.min(10, this.diagramZoom)); // Limit zoom between 10% and 1000%
|
||||
|
||||
this.applyDiagramTransform();
|
||||
}
|
||||
|
||||
resetDiagramZoom() {
|
||||
this.diagramZoom = 1;
|
||||
this.diagramPan = { x: 0, y: 0 };
|
||||
this.applyDiagramTransform();
|
||||
}
|
||||
|
||||
applyDiagramTransform() {
|
||||
if (!this.diagramSvg) return;
|
||||
|
||||
const transform = `scale(${this.diagramZoom}) translate(${this.diagramPan.x}px, ${this.diagramPan.y}px)`;
|
||||
this.diagramSvg.style.transform = transform;
|
||||
this.diagramSvg.style.transformOrigin = 'center center';
|
||||
}
|
||||
|
||||
setupDiagramPanning() {
|
||||
if (!this.elements.diagramContainer) return;
|
||||
|
||||
// Mouse events
|
||||
this.elements.diagramContainer.addEventListener('mousedown', (e) => {
|
||||
if (e.button === 0) { // Left mouse button
|
||||
this.startDragging(e.clientX, e.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (this.isDragging) {
|
||||
this.handleDragging(e.clientX, e.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
this.stopDragging();
|
||||
});
|
||||
|
||||
// Touch events for mobile
|
||||
this.elements.diagramContainer.addEventListener('touchstart', (e) => {
|
||||
if (e.touches.length === 1) {
|
||||
const touch = e.touches[0];
|
||||
this.startDragging(touch.clientX, touch.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('touchmove', (e) => {
|
||||
if (this.isDragging && e.touches.length === 1) {
|
||||
const touch = e.touches[0];
|
||||
this.handleDragging(touch.clientX, touch.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('touchend', () => {
|
||||
this.stopDragging();
|
||||
});
|
||||
|
||||
// Prevent context menu on right click
|
||||
this.elements.diagramContainer.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// Mouse wheel zoom
|
||||
this.elements.diagramContainer.addEventListener('wheel', (e) => {
|
||||
e.preventDefault();
|
||||
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
|
||||
this.zoomDiagram(zoomFactor);
|
||||
});
|
||||
}
|
||||
|
||||
startDragging(x, y) {
|
||||
this.isDragging = true;
|
||||
this.lastMousePos = { x, y };
|
||||
this.elements.diagramContainer.classList.add('dragging');
|
||||
}
|
||||
|
||||
handleDragging(x, y) {
|
||||
if (!this.isDragging) return;
|
||||
|
||||
const deltaX = x - this.lastMousePos.x;
|
||||
const deltaY = y - this.lastMousePos.y;
|
||||
|
||||
// Apply pan delta scaled by zoom level (inverse relationship)
|
||||
this.diagramPan.x += deltaX / this.diagramZoom;
|
||||
this.diagramPan.y += deltaY / this.diagramZoom;
|
||||
|
||||
this.lastMousePos = { x, y };
|
||||
this.applyDiagramTransform();
|
||||
}
|
||||
|
||||
stopDragging() {
|
||||
this.isDragging = false;
|
||||
this.elements.diagramContainer.classList.remove('dragging');
|
||||
} updateLoadMoreButton() {
|
||||
const hasMore = this.state.currentPage < this.state.totalPages;
|
||||
|
||||
if (hasMore && this.state.workflows.length > 0) {
|
||||
|
||||
@@ -529,12 +529,76 @@
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
overflow-x: auto;
|
||||
overflow: visible;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.mermaid svg {
|
||||
max-width: 100%;
|
||||
max-width: none;
|
||||
height: auto;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.diagram-container {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
height: 500px;
|
||||
position: relative;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.diagram-container.dragging {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.diagram-container .mermaid {
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.diagram-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.zoom-btn {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.zoom-btn:hover {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.zoom-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.zoom-info {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-secondary);
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@@ -739,11 +803,18 @@
|
||||
<div class="workflow-detail hidden" id="diagramSection">
|
||||
<div class="section-header">
|
||||
<h4>Workflow Diagram</h4>
|
||||
<button id="copyDiagramBtn" class="copy-btn" title="Copy diagram code to clipboard">
|
||||
📋 Copy
|
||||
</button>
|
||||
<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>
|
||||
</div>
|
||||
<div id="diagramContainer" class="diagram-container">
|
||||
<div id="diagramViewer">Loading diagram...</div>
|
||||
</div>
|
||||
<div id="diagramViewer">Loading diagram...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -806,14 +877,23 @@
|
||||
jsonViewer: document.getElementById('jsonViewer'),
|
||||
diagramSection: document.getElementById('diagramSection'),
|
||||
diagramViewer: document.getElementById('diagramViewer'),
|
||||
diagramContainer: document.getElementById('diagramContainer'),
|
||||
copyJsonBtn: document.getElementById('copyJsonBtn'),
|
||||
copyDiagramBtn: document.getElementById('copyDiagramBtn')
|
||||
copyDiagramBtn: document.getElementById('copyDiagramBtn'),
|
||||
zoomInBtn: document.getElementById('zoomInBtn'),
|
||||
zoomOutBtn: document.getElementById('zoomOutBtn'),
|
||||
zoomResetBtn: document.getElementById('zoomResetBtn')
|
||||
};
|
||||
|
||||
this.searchDebounceTimer = null;
|
||||
this.currentWorkflow = null;
|
||||
this.currentJsonData = null;
|
||||
this.currentDiagramData = null;
|
||||
this.diagramZoom = 1;
|
||||
this.diagramSvg = null;
|
||||
this.diagramPan = { x: 0, y: 0 };
|
||||
this.isDragging = false;
|
||||
this.lastMousePos = { x: 0, y: 0 };
|
||||
this.init();
|
||||
}
|
||||
|
||||
@@ -920,11 +1000,38 @@
|
||||
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();
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
document.addEventListener('keydown', (e) => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1322,6 +1429,10 @@
|
||||
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.viewJsonBtn.textContent = '📄 View JSON';
|
||||
@@ -1382,6 +1493,13 @@
|
||||
// Re-initialize Mermaid for the new diagram
|
||||
if (typeof mermaid !== 'undefined') {
|
||||
mermaid.init(undefined, this.elements.diagramViewer.querySelector('.mermaid'));
|
||||
|
||||
// Store reference to SVG and reset zoom
|
||||
setTimeout(() => {
|
||||
this.diagramSvg = this.elements.diagramViewer.querySelector('.mermaid svg');
|
||||
this.resetDiagramZoom();
|
||||
this.setupDiagramPanning();
|
||||
}, 100);
|
||||
}
|
||||
} catch (error) {
|
||||
this.elements.diagramViewer.textContent = 'Error loading diagram: ' + error.message;
|
||||
@@ -1390,7 +1508,109 @@
|
||||
}
|
||||
}
|
||||
|
||||
updateLoadMoreButton() {
|
||||
zoomDiagram(factor) {
|
||||
if (!this.diagramSvg) return;
|
||||
|
||||
this.diagramZoom *= factor;
|
||||
this.diagramZoom = Math.max(0.1, Math.min(10, this.diagramZoom)); // Limit zoom between 10% and 1000%
|
||||
|
||||
this.applyDiagramTransform();
|
||||
}
|
||||
|
||||
resetDiagramZoom() {
|
||||
this.diagramZoom = 1;
|
||||
this.diagramPan = { x: 0, y: 0 };
|
||||
this.applyDiagramTransform();
|
||||
}
|
||||
|
||||
applyDiagramTransform() {
|
||||
if (!this.diagramSvg) return;
|
||||
|
||||
const transform = `scale(${this.diagramZoom}) translate(${this.diagramPan.x}px, ${this.diagramPan.y}px)`;
|
||||
this.diagramSvg.style.transform = transform;
|
||||
this.diagramSvg.style.transformOrigin = 'center center';
|
||||
}
|
||||
|
||||
setupDiagramPanning() {
|
||||
if (!this.elements.diagramContainer) return;
|
||||
|
||||
// Mouse events
|
||||
this.elements.diagramContainer.addEventListener('mousedown', (e) => {
|
||||
if (e.button === 0) { // Left mouse button
|
||||
this.startDragging(e.clientX, e.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (this.isDragging) {
|
||||
this.handleDragging(e.clientX, e.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
this.stopDragging();
|
||||
});
|
||||
|
||||
// Touch events for mobile
|
||||
this.elements.diagramContainer.addEventListener('touchstart', (e) => {
|
||||
if (e.touches.length === 1) {
|
||||
const touch = e.touches[0];
|
||||
this.startDragging(touch.clientX, touch.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('touchmove', (e) => {
|
||||
if (this.isDragging && e.touches.length === 1) {
|
||||
const touch = e.touches[0];
|
||||
this.handleDragging(touch.clientX, touch.clientY);
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('touchend', () => {
|
||||
this.stopDragging();
|
||||
});
|
||||
|
||||
// Prevent context menu on right click
|
||||
this.elements.diagramContainer.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// Mouse wheel zoom
|
||||
this.elements.diagramContainer.addEventListener('wheel', (e) => {
|
||||
e.preventDefault();
|
||||
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
|
||||
this.zoomDiagram(zoomFactor);
|
||||
});
|
||||
}
|
||||
|
||||
startDragging(x, y) {
|
||||
this.isDragging = true;
|
||||
this.lastMousePos = { x, y };
|
||||
this.elements.diagramContainer.classList.add('dragging');
|
||||
}
|
||||
|
||||
handleDragging(x, y) {
|
||||
if (!this.isDragging) return;
|
||||
|
||||
const deltaX = x - this.lastMousePos.x;
|
||||
const deltaY = y - this.lastMousePos.y;
|
||||
|
||||
// Apply pan delta scaled by zoom level (inverse relationship)
|
||||
this.diagramPan.x += deltaX / this.diagramZoom;
|
||||
this.diagramPan.y += deltaY / this.diagramZoom;
|
||||
|
||||
this.lastMousePos = { x, y };
|
||||
this.applyDiagramTransform();
|
||||
}
|
||||
|
||||
stopDragging() {
|
||||
this.isDragging = false;
|
||||
this.elements.diagramContainer.classList.remove('dragging');
|
||||
} updateLoadMoreButton() {
|
||||
const hasMore = this.state.currentPage < this.state.totalPages;
|
||||
|
||||
if (hasMore && this.state.workflows.length > 0) {
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
{
|
||||
"name": "CalcsLive Demo Workflow Template",
|
||||
"description": "Demonstrates @calcslive/n8n-nodes-calcslive custom node (https://www.npmjs.com/package/@calcslive/n8n-nodes-calcslive) that brings unit-aware physical quantities (PQ) and calculations to the n8n ecosystem in a composable manner. Example workflow with cylinder mass calculations.",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-128,
|
||||
-192
|
||||
],
|
||||
"id": "c6331ca9-2a74-419e-a15f-a11e5f3c0583",
|
||||
"name": "When clicking 'Execute workflow'"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"articleId": "3M6UW7CQB-2AP",
|
||||
"inputPQs": {
|
||||
"pq": [
|
||||
{
|
||||
"symbol": "D",
|
||||
"value": 200,
|
||||
"unit": "mm"
|
||||
},
|
||||
{
|
||||
"symbol": "h",
|
||||
"value": 20,
|
||||
"unit": "cm"
|
||||
}
|
||||
]
|
||||
},
|
||||
"outputPQs": {
|
||||
"pq": [
|
||||
{
|
||||
"symbol": "A",
|
||||
"unit": "m^2"
|
||||
},
|
||||
{
|
||||
"symbol": "V",
|
||||
"unit": "m^3"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "@calcslive/n8n-nodes-calcslive.calcsLive",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
128,
|
||||
-80
|
||||
],
|
||||
"id": "c22f212e-52ef-4d4f-b398-0bd4f2250705",
|
||||
"name": "Cylinder Calcs: (D, h) => (A, V)",
|
||||
"credentials": {
|
||||
"calcsLiveApi": {
|
||||
"id": "REPLACE_WITH_YOUR_CALCSLIVE_CREDENTIAL_ID",
|
||||
"name": "Your CalcsLive API Credential"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"articleId": "3M6UW7CQB-2AP",
|
||||
"inputPQs": {
|
||||
"pq": [
|
||||
{
|
||||
"symbol": "d",
|
||||
"value": 360,
|
||||
"unit": "km"
|
||||
},
|
||||
{
|
||||
"symbol": "t",
|
||||
"value": 10,
|
||||
"unit": "h"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "@calcslive/n8n-nodes-calcslive.calcsLive",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
336,
|
||||
-288
|
||||
],
|
||||
"id": "9b8cc0ea-d130-48a3-8552-4346f20a5ad0",
|
||||
"name": "Speed Calc: (d, t) => v",
|
||||
"credentials": {
|
||||
"calcsLiveApi": {
|
||||
"id": "REPLACE_WITH_YOUR_CALCSLIVE_CREDENTIAL_ID",
|
||||
"name": "Your CalcsLive API Credential"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"articleId": "3M6VLSBHB-3HT",
|
||||
"inputPQs": {
|
||||
"pq": [
|
||||
{
|
||||
"symbol": "ρ",
|
||||
"value": 1000,
|
||||
"unit": "kg/m^3"
|
||||
},
|
||||
{
|
||||
"symbol": "V",
|
||||
"value": "={{ $json.data.calculation.outputs.V.value }}",
|
||||
"unit": "={{ $json.data.calculation.outputs.V.unit }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"outputPQs": {
|
||||
"pq": [
|
||||
{
|
||||
"symbol": "m",
|
||||
"unit": "kg"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "@calcslive/n8n-nodes-calcslive.calcsLive",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
336,
|
||||
-80
|
||||
],
|
||||
"id": "5bf3e5ab-d1f6-42e7-9e64-b4b78bcbfa99",
|
||||
"name": "Mass Calc: (ρ, V) => m",
|
||||
"credentials": {
|
||||
"calcsLiveApi": {
|
||||
"id": "REPLACE_WITH_YOUR_CALCSLIVE_CREDENTIAL_ID",
|
||||
"name": "Your CalcsLive API Credential"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "a026dc84-665f-4898-8de9-ccdbaa530bfa",
|
||||
"name": "Distance",
|
||||
"value": 360,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"id": "de7d6d3e-151c-4390-b2d9-bf78da0159eb",
|
||||
"name": "DistanceUnit",
|
||||
"value": "km",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"id": "49c1e979-a0c2-403e-bed1-ef28eb8513ad",
|
||||
"name": "Time",
|
||||
"value": 2,
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"id": "54ae6f42-844f-4bf2-b6d3-a6e159d46e9e",
|
||||
"name": "TimeUnit",
|
||||
"value": "h",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.4,
|
||||
"position": [
|
||||
128,
|
||||
-288
|
||||
],
|
||||
"id": "8b83b9e5-d7a4-4f74-b5bc-e6012a0f606a",
|
||||
"name": "Fields: (d, t)"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"sendTo": "user@example.com",
|
||||
"subject": "CalcsLive Calculation Results",
|
||||
"message": "=Hello!\n\nThis is an automated email from your n8n workflow using @calcslive/n8n-nodes-calcslive.\n\nCalculation Results:\n- Total Physical Quantities: {{ $json.data.calculation.totalPQs }}\n- Mass Result: {{ $json.data.calculation.outputs.m.value }} {{ $json.data.calculation.outputs.m.unit }}\n\nBest regards,\nYour CalcsLive Workflow",
|
||||
"options": {
|
||||
"attachmentsUi": {
|
||||
"attachmentsBinary": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "2b48a81e-84f8-439d-aa58-6cc7b1c00480",
|
||||
"name": "Send Email",
|
||||
"type": "n8n-nodes-base.gmail",
|
||||
"typeVersion": 2.1,
|
||||
"position": [
|
||||
528,
|
||||
-80
|
||||
],
|
||||
"credentials": {
|
||||
"gmailOAuth2": {
|
||||
"id": "REPLACE_WITH_YOUR_GMAIL_CREDENTIAL_ID",
|
||||
"name": "Your Gmail Account"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"When clicking 'Execute workflow'": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Fields: (d, t)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Cylinder Calcs: (D, h) => (A, V)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Speed Calc: (d, t) => v": {
|
||||
"main": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
"Mass Calc: (ρ, V) => m": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send Email",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Fields: (d, t)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Speed Calc: (d, t) => v",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cylinder Calcs: (D, h) => (A, V)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Mass Calc: (ρ, V) => m",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": false
|
||||
},
|
||||
"tags": [
|
||||
"calculation",
|
||||
"calculator",
|
||||
"math",
|
||||
"engineering",
|
||||
"unit-conversion",
|
||||
"unit-converter",
|
||||
"unit-aware",
|
||||
"unit-awareness",
|
||||
"physics",
|
||||
"formula",
|
||||
"computation",
|
||||
"measurement",
|
||||
"custom-node",
|
||||
"PQ",
|
||||
"physical-quantity",
|
||||
"integration",
|
||||
"composable"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user