Comfyui-Basic / index.html
Luis-Filipe's picture
Upload index.html
a7d0ef2 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ComfyBASIC - Visual BASIC Programming Environment</title>
<style>
:root {
--bg-dark: #1e1e1e;
--bg-darker: #121212;
--bg-panel: #2d2d2d;
--bg-node: #333333;
--border-color: #444;
--accent-primary: #646cff;
--accent-secondary: #ff6b6b;
--text-primary: #ffffff;
--text-secondary: #cccccc;
--node-input: #4caf50;
--node-output: #2196f3;
--node-process: #ff9800;
--node-control: #9c27b0;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: var(--bg-darker);
color: var(--text-primary);
height: 100vh;
overflow: hidden;
}
.app-container {
display: flex;
flex-direction: column;
height: 100vh;
}
.header {
background-color: var(--bg-dark);
padding: 12px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--border-color);
}
.logo {
display: flex;
align-items: center;
gap: 10px;
font-size: 1.5rem;
font-weight: bold;
color: var(--accent-primary);
}
.controls {
display: flex;
gap: 10px;
}
.btn {
background-color: var(--bg-panel);
color: var(--text-primary);
border: 1px solid var(--border-color);
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 6px;
}
.btn:hover {
background-color: var(--accent-primary);
border-color: var(--accent-primary);
}
.btn-primary {
background-color: var(--accent-primary);
border-color: var(--accent-primary);
}
.btn-primary:hover {
background-color: #535bf2;
}
.btn-success {
background-color: var(--node-input);
border-color: var(--node-input);
}
.btn-success:hover {
background-color: #3d8b40;
}
.main-content {
display: flex;
flex: 1;
overflow: hidden;
}
.sidebar {
width: 250px;
background-color: var(--bg-dark);
border-right: 1px solid var(--border-color);
padding: 20px 0;
overflow-y: auto;
}
.sidebar-title {
padding: 0 20px 10px;
font-size: 1.1rem;
color: var(--text-secondary);
border-bottom: 1px solid var(--border-color);
margin-bottom: 15px;
}
.node-category {
margin-bottom: 20px;
}
.category-title {
padding: 5px 20px;
font-weight: bold;
color: var(--text-secondary);
font-size: 0.9rem;
text-transform: uppercase;
}
.node-item {
padding: 10px 20px;
cursor: grab;
transition: background 0.2s;
display: flex;
align-items: center;
gap: 10px;
}
.node-item:hover {
background-color: var(--bg-panel);
}
.node-icon {
width: 20px;
height: 20px;
border-radius: 4px;
}
.workspace-container {
display: flex;
flex: 1;
overflow: hidden;
}
.workspace {
flex: 1;
position: relative;
background-color: var(--bg-darker);
overflow: hidden;
}
.workspace-grid {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(68, 68, 68, 0.2) 1px, transparent 1px),
linear-gradient(90deg, rgba(68, 68, 68, 0.2) 1px, transparent 1px);
background-size: 20px 20px;
}
.node {
position: absolute;
background-color: var(--bg-node);
border: 1px solid var(--border-color);
border-radius: 6px;
min-width: 180px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
cursor: move;
z-index: 10;
}
.node-header {
padding: 10px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
}
.node-title {
font-weight: 500;
font-size: 0.9rem;
}
.node-close {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
font-size: 1.2rem;
}
.node-close:hover {
color: var(--accent-secondary);
}
.node-content {
padding: 15px;
}
.node-inputs, .node-outputs {
display: flex;
flex-direction: column;
gap: 8px;
}
.io-item {
display: flex;
align-items: center;
gap: 8px;
}
.io-connector {
width: 12px;
height: 12px;
border-radius: 50%;
cursor: crosshair;
}
.input-connector {
background-color: var(--node-input);
}
.output-connector {
background-color: var(--node-output);
}
.io-label {
font-size: 0.85rem;
}
.node-body {
padding: 10px 0;
}
.node-param {
margin-bottom: 10px;
}
.param-label {
display: block;
font-size: 0.8rem;
margin-bottom: 4px;
color: var(--text-secondary);
}
.param-input {
width: 100%;
padding: 6px;
background-color: var(--bg-dark);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
}
.connections-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 5;
}
.connection-line {
stroke: var(--accent-primary);
stroke-width: 2;
fill: none;
}
.output-panel {
width: 300px;
background-color: var(--bg-dark);
border-left: 1px solid var(--border-color);
display: flex;
flex-direction: column;
}
.output-header {
padding: 12px;
border-bottom: 1px solid var(--border-color);
font-weight: bold;
}
.output-content {
flex: 1;
padding: 15px;
overflow-y: auto;
font-family: monospace;
white-space: pre-wrap;
background-color: var(--bg-darker);
}
.status-bar {
background-color: var(--bg-dark);
padding: 8px 20px;
border-top: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
font-size: 0.9rem;
color: var(--text-secondary);
}
.node-process { border-left: 3px solid var(--node-process); }
.node-control { border-left: 3px solid var(--node-control); }
.node-io { border-left: 3px solid var(--node-output); }
.console-output {
color: #00ff00;
font-size: 14px;
line-height: 1.4;
}
.console-input {
color: #ffff00;
}
.console-error {
color: #ff5555;
}
</style>
</head>
<body>
<div class="app-container">
<div class="header">
<div class="logo">
<span>⚙️</span>
<span>ComfyBASIC</span>
</div>
<div class="controls">
<button class="btn" id="clearBtn">
<span>🗑️</span>
Clear
</button>
<button class="btn" id="downloadBtn">
<span>💾</span>
Download
</button>
<button class="btn btn-success" id="runBtn">
<span>▶️</span>
Run Program
</button>
</div>
</div>
<div class="main-content">
<div class="sidebar">
<div class="sidebar-title">NODE LIBRARY</div>
<div class="node-category">
<div class="category-title">Input/Output</div>
<div class="node-item" data-node="print">
<div class="node-icon" style="background-color: var(--node-output);"></div>
<span>Print Statement</span>
</div>
<div class="node-item" data-node="input">
<div class="node-icon" style="background-color: var(--node-input);"></div>
<span>Input Statement</span>
</div>
</div>
<div class="node-category">
<div class="category-title">Variables</div>
<div class="node-item" data-node="variable">
<div class="node-icon" style="background-color: var(--node-process);"></div>
<span>Variable Assignment</span>
</div>
<div class="node-item" data-node="getvar">
<div class="node-icon" style="background-color: var(--node-process);"></div>
<span>Get Variable</span>
</div>
</div>
<div class="node-category">
<div class="category-title">Control Flow</div>
<div class="node-item" data-node="if">
<div class="node-icon" style="background-color: var(--node-control);"></div>
<span>If Statement</span>
</div>
<div class="node-item" data-node="for">
<div class="node-icon" style="background-color: var(--node-control);"></div>
<span>For Loop</span>
</div>
</div>
<div class="node-category">
<div class="category-title">Math Operations</div>
<div class="node-item" data-node="add">
<div class="node-icon" style="background-color: var(--node-process);"></div>
<span>Addition</span>
</div>
<div class="node-item" data-node="multiply">
<div class="node-icon" style="background-color: var(--node-process);"></div>
<span>Multiplication</span>
</div>
</div>
<div class="node-category">
<div class="category-title">Values</div>
<div class="node-item" data-node="number">
<div class="node-icon" style="background-color: var(--node-input);"></div>
<span>Number</span>
</div>
<div class="node-item" data-node="string">
<div class="node-icon" style="background-color: var(--node-input);"></div>
<span>String</span>
</div>
</div>
</div>
<div class="workspace-container">
<div class="workspace">
<div class="workspace-grid" id="workspaceGrid"></div>
<svg class="connections-container" id="connectionsContainer"></svg>
<!-- Nodes will be added here dynamically -->
</div>
<div class="output-panel">
<div class="output-header">PROGRAM OUTPUT</div>
<div class="output-content" id="outputContent">
<!-- Output will appear here -->
</div>
</div>
</div>
</div>
<div class="status-bar">
<div class="status-left">Ready</div>
<div class="status-right">ComfyBASIC v1.0</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const workspace = document.querySelector('.workspace');
const nodeItems = document.querySelectorAll('.node-item');
const clearBtn = document.getElementById('clearBtn');
const downloadBtn = document.getElementById('downloadBtn');
const runBtn = document.getElementById('runBtn');
const outputContent = document.getElementById('outputContent');
const connectionsContainer = document.getElementById('connectionsContainer');
let nodeId = 0;
let connections = [];
let isConnecting = false;
let startConnector = null;
let tempLine = null;
let nodeData = {};
// Node templates
const nodeTemplates = {
print: {
title: "Print Statement",
inputs: [{id: "value", label: "Value"}],
outputs: [],
params: [],
color: "var(--node-output)"
},
input: {
title: "Input Statement",
inputs: [],
outputs: [{id: "value", label: "Input Value"}],
params: [{id: "prompt", label: "Prompt", type: "text", value: "Enter value:"}],
color: "var(--node-input)"
},
variable: {
title: "Variable Assignment",
inputs: [{id: "value", label: "Value"}],
outputs: [],
params: [{id: "name", label: "Variable Name", type: "text", value: "X"}],
color: "var(--node-process)"
},
getvar: {
title: "Get Variable",
inputs: [],
outputs: [{id: "value", label: "Variable Value"}],
params: [{id: "name", label: "Variable Name", type: "text", value: "X"}],
color: "var(--node-process)"
},
if: {
title: "If Statement",
inputs: [{id: "condition", label: "Condition"}],
outputs: [
{id: "true", label: "True Branch"},
{id: "false", label: "False Branch"}
],
params: [{id: "expression", label: "Expression", type: "text", value: "X > 0"}],
color: "var(--node-control)"
},
for: {
title: "For Loop",
inputs: [],
outputs: [{id: "body", label: "Loop Body"}],
params: [
{id: "variable", label: "Counter", type: "text", value: "I"},
{id: "start", label: "Start", type: "number", value: "1"},
{id: "end", label: "End", type: "number", value: "10"}
],
color: "var(--node-control)"
},
add: {
title: "Addition",
inputs: [
{id: "a", label: "Value A"},
{id: "b", label: "Value B"}
],
outputs: [{id: "result", label: "Result"}],
params: [],
color: "var(--node-process)"
},
multiply: {
title: "Multiplication",
inputs: [
{id: "a", label: "Value A"},
{id: "b", label: "Value B"}
],
outputs: [{id: "result", label: "Result"}],
params: [],
color: "var(--node-process)"
},
number: {
title: "Number",
inputs: [],
outputs: [{id: "value", label: "Number Value"}],
params: [{id: "value", label: "Number", type: "number", value: "0"}],
color: "var(--node-input)"
},
string: {
title: "String",
inputs: [],
outputs: [{id: "value", label: "String Value"}],
params: [{id: "value", label: "Text", type: "text", value: "Hello"}],
color: "var(--node-input)"
}
};
// Make sidebar items draggable
nodeItems.forEach(item => {
item.addEventListener('dragstart', function(e) {
e.dataTransfer.setData('text/plain', this.dataset.node);
});
});
// Enable dropping in workspace
workspace.addEventListener('dragover', function(e) {
e.preventDefault();
});
workspace.addEventListener('drop', function(e) {
e.preventDefault();
const nodeType = e.dataTransfer.getData('text/plain');
if (nodeType) {
createNode(nodeType, e.clientX - workspace.getBoundingClientRect().left, e.clientY - workspace.getBoundingClientRect().top);
}
});
// Create a new node
function createNode(type, x, y) {
const template = nodeTemplates[type];
if (!template) return;
nodeId++;
const nodeIdStr = `node-${nodeId}`;
nodeData[nodeIdStr] = {
type: type,
params: {}
};
const node = document.createElement('div');
node.className = 'node';
node.id = nodeIdStr;
node.style.left = `${x}px`;
node.style.top = `${y}px`;
node.innerHTML = `
<div class="node-header" style="border-left: 3px solid ${template.color}">
<div class="node-title">${template.title}</div>
<button class="node-close">×</button>
</div>
<div class="node-content">
${template.inputs.length > 0 ? `
<div class="node-inputs">
${template.inputs.map(input => `
<div class="io-item">
<div class="io-connector input-connector" data-node="${nodeIdStr}" data-io="${input.id}"></div>
<div class="io-label">${input.label}</div>
</div>
`).join('')}
</div>` : ''}
<div class="node-body">
${template.params.map(param => `
<div class="node-param">
<label class="param-label">${param.label}</label>
<input type="${param.type}" class="param-input" data-param="${param.id}" value="${param.value}">
</div>
`).join('')}
</div>
${template.outputs.length > 0 ? `
<div class="node-outputs">
${template.outputs.map(output => `
<div class="io-item">
<div class="io-label">${output.label}</div>
<div class="io-connector output-connector" data-node="${nodeIdStr}" data-io="${output.id}"></div>
</div>
`).join('')}
</div>` : ''}
</div>
`;
workspace.appendChild(node);
// Store initial parameter values
template.params.forEach(param => {
nodeData[nodeIdStr].params[param.id] = param.value;
});
// Add event listeners for the new node
const closeBtn = node.querySelector('.node-close');
closeBtn.addEventListener('click', function() {
deleteNode(nodeIdStr);
});
// Add parameter change listeners
const paramInputs = node.querySelectorAll('.param-input');
paramInputs.forEach(input => {
input.addEventListener('change', function() {
const paramId = this.dataset.param;
nodeData[nodeIdStr].params[paramId] = this.value;
});
});
// Make node draggable
makeNodeDraggable(node);
// Add connection handlers
const connectors = node.querySelectorAll('.io-connector');
connectors.forEach(connector => {
connector.addEventListener('mousedown', startConnection);
});
}
// Delete a node and its connections
function deleteNode(nodeId) {
// Remove connections associated with this node
connections = connections.filter(conn => {
if (conn.from.nodeId === nodeId || conn.to.nodeId === nodeId) {
// Remove SVG line
const line = document.getElementById(conn.id);
if (line) line.remove();
return false;
}
return true;
});
// Remove node data
delete nodeData[nodeId];
// Remove node element
const node = document.getElementById(nodeId);
if (node) node.remove();
}
// Make nodes draggable
function makeNodeDraggable(node) {
let isDragging = false;
let offsetX, offsetY;
const header = node.querySelector('.node-header');
header.addEventListener('mousedown', function(e) {
if (e.target.classList.contains('node-close')) return;
isDragging = true;
const rect = node.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
node.style.zIndex = 1000;
});
document.addEventListener('mousemove', function(e) {
if (!isDragging) return;
const workspaceRect = workspace.getBoundingClientRect();
const x = e.clientX - workspaceRect.left - offsetX;
const y = e.clientY - workspaceRect.top - offsetY;
node.style.left = `${x}px`;
node.style.top = `${y}px`;
// Update connections
updateConnections();
});
document.addEventListener('mouseup', function() {
isDragging = false;
node.style.zIndex = 10;
});
}
// Connection handling
function startConnection(e) {
isConnecting = true;
startConnector = e.target;
// Create temporary line
tempLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
tempLine.classList.add('connection-line');
tempLine.setAttribute('id', 'temp-line');
connectionsContainer.appendChild(tempLine);
updateTempLine(e);
document.addEventListener('mousemove', updateTempLine);
document.addEventListener('mouseup', finishConnection);
}
function updateTempLine(e) {
if (!isConnecting || !startConnector) return;
const startRect = startConnector.getBoundingClientRect();
const workspaceRect = workspace.getBoundingClientRect();
const startX = startRect.left + startRect.width/2 - workspaceRect.left;
const startY = startRect.top + startRect.height/2 - workspaceRect.top;
tempLine.setAttribute('x1', startX);
tempLine.setAttribute('y1', startY);
tempLine.setAttribute('x2', e.clientX - workspaceRect.left);
tempLine.setAttribute('y2', e.clientY - workspaceRect.top);
}
function finishConnection(e) {
if (!isConnecting) return;
isConnecting = false;
document.removeEventListener('mousemove', updateTempLine);
document.removeEventListener('mouseup', finishConnection);
// Remove temporary line
if (tempLine) {
tempLine.remove();
}
// Check if we're over a connector
const element = document.elementFromPoint(e.clientX, e.clientY);
if (element && element.classList.contains('io-connector') && element !== startConnector) {
// Create permanent connection
createConnection(startConnector, element);
}
startConnector = null;
tempLine = null;
}
function createConnection(from, to) {
// Prevent connecting input to input or output to output
const fromIsOutput = from.classList.contains('output-connector');
const toIsInput = to.classList.contains('input-connector');
if (!(fromIsOutput && toIsInput)) return;
// Check if connection already exists
const existingConnection = connections.find(conn =>
conn.from.nodeId === from.dataset.node &&
conn.from.ioId === from.dataset.io &&
conn.to.nodeId === to.dataset.node &&
conn.to.ioId === to.dataset.io
);
if (existingConnection) return;
const connectionId = `conn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
connections.push({
id: connectionId,
from: {
nodeId: from.dataset.node,
ioId: from.dataset.io
},
to: {
nodeId: to.dataset.node,
ioId: to.dataset.io
}
});
drawConnection(from, to, connectionId);
}
function drawConnection(from, to, id) {
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.classList.add('connection-line');
line.setAttribute('id', id);
connectionsContainer.appendChild(line);
updateConnectionLine(from, to, line);
}
function updateConnectionLine(from, to, line) {
const fromRect = from.getBoundingClientRect();
const toRect = to.getBoundingClientRect();
const workspaceRect = workspace.getBoundingClientRect();
const fromX = fromRect.left + fromRect.width/2 - workspaceRect.left;
const fromY = fromRect.top + fromRect.height/2 - workspaceRect.top;
const toX = toRect.left + toRect.width/2 - workspaceRect.left;
const toY = toRect.top + toRect.height/2 - workspaceRect.top;
line.setAttribute('x1', fromX);
line.setAttribute('y1', fromY);
line.setAttribute('x2', toX);
line.setAttribute('y2', toY);
}
function updateConnections() {
connections.forEach(connection => {
const fromNode = document.querySelector(`.io-connector[data-node="${connection.from.nodeId}"][data-io="${connection.from.ioId}"]`);
const toNode = document.querySelector(`.io-connector[data-node="${connection.to.nodeId}"][data-io="${connection.to.ioId}"]`);
if (fromNode && toNode) {
const line = document.getElementById(connection.id);
if (line) {
updateConnectionLine(fromNode, toNode, line);
}
}
});
}
// Clear workspace
clearBtn.addEventListener('click', function() {
document.querySelectorAll('.node').forEach(node => node.remove());
document.querySelectorAll('.connection-line').forEach(line => line.remove());
connections = [];
nodeData = {};
nodeId = 0;
outputContent.innerHTML = '';
});
// Download program
downloadBtn.addEventListener('click', function() {
const program = generateBASICProgram();
const blob = new Blob([program], {type: 'text/plain'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'program.bas';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
// Run program
runBtn.addEventListener('click', function() {
runProgram();
});
// Generate BASIC program from nodes
function generateBASICProgram() {
let program = [];
program.push("10 REM Generated by ComfyBASIC");
program.push("20 REM Program Start");
let lineNum = 30;
const processedNodes = new Set();
// Find all print nodes and trace back their inputs
const printNodes = Array.from(document.querySelectorAll('.node')).filter(node => {
const nodeId = node.id;
const nodeInfo = nodeData[nodeId];
return nodeInfo && nodeInfo.type === 'print';
});
// Process each print node
printNodes.forEach(printNode => {
const nodeId = printNode.id;
// Find input connection to print node
const inputConn = connections.find(conn =>
conn.to.nodeId === nodeId && conn.to.ioId === 'value'
);
if (inputConn) {
const valueExpr = getNodeExpression(inputConn.from.nodeId);
if (valueExpr) {
program.push(`${lineNum} PRINT ${valueExpr}`);
lineNum += 10;
}
} else {
program.push(`${lineNum} PRINT "Hello from ComfyBASIC"`);
lineNum += 10;
}
});
// Handle input nodes that aren't connected to anything
const inputNodes = Array.from(document.querySelectorAll('.node')).filter(node => {
const nodeId = node.id;
const nodeInfo = nodeData[nodeId];
return nodeInfo && nodeInfo.type === 'input';
});
inputNodes.forEach(inputNode => {
const nodeId = inputNode.id;
// Check if this input node is connected to anything
const hasOutputConnections = connections.some(conn =>
conn.from.nodeId === nodeId
);
if (!hasOutputConnections) {
const nodeInfo = nodeData[nodeId];
const prompt = nodeInfo.params.prompt || 'Enter value:';
program.push(`${lineNum} INPUT "${prompt}", TEMP${nodeId.split('-')[1]}`);
lineNum += 10;
}
});
program.push(`${lineNum} END`);
return program.join('\n') + '\n';
}
// Get expression for a node
function getNodeExpression(nodeId) {
const nodeInfo = nodeData[nodeId];
if (!nodeInfo) return null;
switch (nodeInfo.type) {
case 'getvar':
return nodeInfo.params.name || 'X';
case 'number':
return nodeInfo.params.value || '0';
case 'string':
return `"${nodeInfo.params.value || 'Hello'}"`;
case 'add':
const aConn = connections.find(conn =>
conn.to.nodeId === nodeId && conn.to.ioId === 'a'
);
const bConn = connections.find(conn =>
conn.to.nodeId === nodeId && conn.to.ioId === 'b'
);
const aExpr = aConn ? getNodeExpression(aConn.from.nodeId) : '0';
const bExpr = bConn ? getNodeExpression(bConn.from.nodeId) : '0';
return `${aExpr} + ${bExpr}`;
case 'multiply':
const mulAConn = connections.find(conn =>
conn.to.nodeId === nodeId && conn.to.ioId === 'a'
);
const mulBConn = connections.find(conn =>
conn.to.nodeId === nodeId && conn.to.ioId === 'b'
);
const mulAExpr = mulAConn ? getNodeExpression(mulAConn.from.nodeId) : '1';
const mulBExpr = mulBConn ? getNodeExpression(mulBConn.from.nodeId) : '1';
return `${mulAExpr} * ${mulBExpr}`;
default:
return null;
}
}
// Run the program in the browser
function runProgram() {
outputContent.innerHTML = '';
const program = generateBASICProgram();
const lines = program.split('\n');
// Display program header
appendToOutput("ComfyBASIC Interpreter v1.0\n", "console-output");
appendToOutput("Running program...\n", "console-output");
appendToOutput("--------------------\n", "console-output");
// Simple BASIC interpreter
let variables = {};
let lineIndex = 0;
function executeLine(line) {
const trimmedLine = line.trim();
if (!trimmedLine || trimmedLine.startsWith('REM')) return true;
// Parse line number and command
const match = trimmedLine.match(/^(\d+)\s+(.*)$/);
if (!match) return true;
const command = match[2];
if (command.startsWith('PRINT')) {
const expr = command.substring(6).trim();
if (expr.startsWith('"') && expr.endsWith('"')) {
// String literal
appendToOutput(expr.substring(1, expr.length - 1) + '\n', "console-output");
} else {
// Variable or expression
try {
const result = evaluateExpression(expr, variables);
appendToOutput(result + '\n', "console-output");
} catch (e) {
appendToOutput(`Error: ${e.message}\n`, "console-error");
}
}
} else if (command.startsWith('INPUT')) {
const parts = command.substring(6).split(',');
const prompt = parts[0].trim().replace(/"/g, '');
const varName = parts[1].trim();
appendToOutput(prompt + ' ', "console-input");
// In a real implementation, we'd wait for input
// For demo, we'll just assign a default value
variables[varName] = Math.floor(Math.random() * 100);
appendToOutput(variables[varName] + '\n', "console-output");
} else if (command.startsWith('LET')) {
const expr = command.substring(4).trim();
const eqIndex = expr.indexOf('=');
if (eqIndex > 0) {
const varName = expr.substring(0, eqIndex).trim();
const valueExpr = expr.substring(eqIndex + 1).trim();
variables[varName] = evaluateExpression(valueExpr, variables);
}
} else if (command === 'END') {
return false;
}
return true;
}
function evaluateExpression(expr, vars) {
// Simple expression evaluator (supports +, -, *, /, variables, and numbers)
expr = expr.replace(/\s+/g, '');
// Replace variables with their values
for (const [varName, value] of Object.entries(vars)) {
const regex = new RegExp(`\\b${varName}\\b`, 'g');
expr = expr.replace(regex, value);
}
try {
// This is a very simple evaluator - in reality you'd want a proper parser
return Function('"use strict"; return (' + expr + ')')();
} catch (e) {
throw new Error(`Cannot evaluate expression: ${expr}`);
}
}
function appendToOutput(text, className) {
const span = document.createElement('span');
span.className = className;
span.textContent = text;
outputContent.appendChild(span);
outputContent.scrollTop = outputContent.scrollHeight;
}
// Execute program line by line
for (const line of lines) {
if (!executeLine(line)) break;
}
appendToOutput("--------------------\n", "console-output");
appendToOutput("Program finished.\n", "console-output");
}
// Create some initial nodes for demo
setTimeout(() => {
createNode('number', 150, 100);
createNode('number', 150, 200);
createNode('add', 350, 150);
createNode('print', 550, 150);
// Set parameters
document.querySelector('#node-1 .param-input[data-param="value"]').value = '5';
nodeData['node-1'].params.value = '5';
document.querySelector('#node-2 .param-input[data-param="value"]').value = '3';
nodeData['node-2'].params.value = '3';
// Auto-connect nodes
setTimeout(() => {
const num1Output = document.querySelector('.io-connector.output-connector[data-node="node-1"]');
const addInputA = document.querySelector('.io-connector.input-connector[data-node="node-3"][data-io="a"]');
const num2Output = document.querySelector('.io-connector.output-connector[data-node="node-2"]');
const addInputB = document.querySelector('.io-connector.input-connector[data-node="node-3"][data-io="b"]');
const addOutput = document.querySelector('.io-connector.output-connector[data-node="node-3"]');
const printInput = document.querySelector('.io-connector.input-connector[data-node="node-4"][data-io="value"]');
if (num1Output && addInputA) createConnection(num1Output, addInputA);
if (num2Output && addInputB) createConnection(num2Output, addInputB);
if (addOutput && printInput) createConnection(addOutput, printInput);
}, 100);
}, 500);
});
</script>
</body>
</html>