Files
crawl4ai/docs/examples/c4a_script/tutorial/assets/c4a-generator.js
UncleCode 08a2cdae53 Add C4A-Script support and documentation
- Generate OneShot js code geenrator
- Introduced a new C4A-Script tutorial example for login flow using Blockly.
- Updated index.html to include Blockly theme and event editor modal for script editing.
- Created a test HTML file for testing Blockly integration.
- Added comprehensive C4A-Script API reference documentation covering commands, syntax, and examples.
- Developed core documentation for C4A-Script, detailing its features, commands, and real-world examples.
- Updated mkdocs.yml to include new C4A-Script documentation in navigation.
2025-06-07 23:07:19 +08:00

261 lines
9.0 KiB
JavaScript

// C4A-Script Code Generator for Blockly
// Compatible with latest Blockly API
// Create a custom code generator for C4A-Script
const c4aGenerator = new Blockly.Generator('C4A');
// Helper to get field value with proper escaping
c4aGenerator.getFieldValue = function(block, fieldName) {
return block.getFieldValue(fieldName);
};
// Navigation generators
c4aGenerator.forBlock['c4a_go'] = function(block, generator) {
const url = generator.getFieldValue(block, 'URL');
return `GO ${url}\n`;
};
c4aGenerator.forBlock['c4a_reload'] = function(block, generator) {
return 'RELOAD\n';
};
c4aGenerator.forBlock['c4a_back'] = function(block, generator) {
return 'BACK\n';
};
c4aGenerator.forBlock['c4a_forward'] = function(block, generator) {
return 'FORWARD\n';
};
// Wait generators
c4aGenerator.forBlock['c4a_wait_time'] = function(block, generator) {
const seconds = generator.getFieldValue(block, 'SECONDS');
return `WAIT ${seconds}\n`;
};
c4aGenerator.forBlock['c4a_wait_selector'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
const timeout = generator.getFieldValue(block, 'TIMEOUT');
return `WAIT \`${selector}\` ${timeout}\n`;
};
c4aGenerator.forBlock['c4a_wait_text'] = function(block, generator) {
const text = generator.getFieldValue(block, 'TEXT');
const timeout = generator.getFieldValue(block, 'TIMEOUT');
return `WAIT "${text}" ${timeout}\n`;
};
// Mouse action generators
c4aGenerator.forBlock['c4a_click'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
return `CLICK \`${selector}\`\n`;
};
c4aGenerator.forBlock['c4a_click_xy'] = function(block, generator) {
const x = generator.getFieldValue(block, 'X');
const y = generator.getFieldValue(block, 'Y');
return `CLICK ${x} ${y}\n`;
};
c4aGenerator.forBlock['c4a_double_click'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
return `DOUBLE_CLICK \`${selector}\`\n`;
};
c4aGenerator.forBlock['c4a_right_click'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
return `RIGHT_CLICK \`${selector}\`\n`;
};
c4aGenerator.forBlock['c4a_move'] = function(block, generator) {
const x = generator.getFieldValue(block, 'X');
const y = generator.getFieldValue(block, 'Y');
return `MOVE ${x} ${y}\n`;
};
c4aGenerator.forBlock['c4a_drag'] = function(block, generator) {
const x1 = generator.getFieldValue(block, 'X1');
const y1 = generator.getFieldValue(block, 'Y1');
const x2 = generator.getFieldValue(block, 'X2');
const y2 = generator.getFieldValue(block, 'Y2');
return `DRAG ${x1} ${y1} ${x2} ${y2}\n`;
};
c4aGenerator.forBlock['c4a_scroll'] = function(block, generator) {
const direction = generator.getFieldValue(block, 'DIRECTION');
const amount = generator.getFieldValue(block, 'AMOUNT');
return `SCROLL ${direction} ${amount}\n`;
};
// Keyboard generators
c4aGenerator.forBlock['c4a_type'] = function(block, generator) {
const text = generator.getFieldValue(block, 'TEXT');
return `TYPE "${text}"\n`;
};
c4aGenerator.forBlock['c4a_type_var'] = function(block, generator) {
const varName = generator.getFieldValue(block, 'VAR');
return `TYPE $${varName}\n`;
};
c4aGenerator.forBlock['c4a_clear'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
return `CLEAR \`${selector}\`\n`;
};
c4aGenerator.forBlock['c4a_set'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
const value = generator.getFieldValue(block, 'VALUE');
return `SET \`${selector}\` "${value}"\n`;
};
c4aGenerator.forBlock['c4a_press'] = function(block, generator) {
const key = generator.getFieldValue(block, 'KEY');
return `PRESS ${key}\n`;
};
c4aGenerator.forBlock['c4a_key_down'] = function(block, generator) {
const key = generator.getFieldValue(block, 'KEY');
return `KEY_DOWN ${key}\n`;
};
c4aGenerator.forBlock['c4a_key_up'] = function(block, generator) {
const key = generator.getFieldValue(block, 'KEY');
return `KEY_UP ${key}\n`;
};
// Control flow generators
c4aGenerator.forBlock['c4a_if_exists'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
const thenCode = generator.statementToCode(block, 'THEN').trim();
if (thenCode.includes('\n')) {
// Multi-line then block
const lines = thenCode.split('\n').filter(line => line.trim());
return lines.map(line => `IF (EXISTS \`${selector}\`) THEN ${line}`).join('\n') + '\n';
} else if (thenCode) {
// Single line
return `IF (EXISTS \`${selector}\`) THEN ${thenCode}\n`;
}
return '';
};
c4aGenerator.forBlock['c4a_if_exists_else'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
const thenCode = generator.statementToCode(block, 'THEN').trim();
const elseCode = generator.statementToCode(block, 'ELSE').trim();
// For simplicity, only handle single-line then/else
const thenLine = thenCode.split('\n')[0];
const elseLine = elseCode.split('\n')[0];
if (thenLine && elseLine) {
return `IF (EXISTS \`${selector}\`) THEN ${thenLine} ELSE ${elseLine}\n`;
} else if (thenLine) {
return `IF (EXISTS \`${selector}\`) THEN ${thenLine}\n`;
}
return '';
};
c4aGenerator.forBlock['c4a_if_not_exists'] = function(block, generator) {
const selector = generator.getFieldValue(block, 'SELECTOR');
const thenCode = generator.statementToCode(block, 'THEN').trim();
if (thenCode.includes('\n')) {
const lines = thenCode.split('\n').filter(line => line.trim());
return lines.map(line => `IF (NOT EXISTS \`${selector}\`) THEN ${line}`).join('\n') + '\n';
} else if (thenCode) {
return `IF (NOT EXISTS \`${selector}\`) THEN ${thenCode}\n`;
}
return '';
};
c4aGenerator.forBlock['c4a_if_js'] = function(block, generator) {
const condition = generator.getFieldValue(block, 'CONDITION');
const thenCode = generator.statementToCode(block, 'THEN').trim();
if (thenCode.includes('\n')) {
const lines = thenCode.split('\n').filter(line => line.trim());
return lines.map(line => `IF (\`${condition}\`) THEN ${line}`).join('\n') + '\n';
} else if (thenCode) {
return `IF (\`${condition}\`) THEN ${thenCode}\n`;
}
return '';
};
c4aGenerator.forBlock['c4a_repeat_times'] = function(block, generator) {
const times = generator.getFieldValue(block, 'TIMES');
const doCode = generator.statementToCode(block, 'DO').trim();
if (doCode) {
// Get first command for repeat
const firstLine = doCode.split('\n')[0];
return `REPEAT (${firstLine}, ${times})\n`;
}
return '';
};
c4aGenerator.forBlock['c4a_repeat_while'] = function(block, generator) {
const condition = generator.getFieldValue(block, 'CONDITION');
const doCode = generator.statementToCode(block, 'DO').trim();
if (doCode) {
// Get first command for repeat
const firstLine = doCode.split('\n')[0];
return `REPEAT (${firstLine}, \`${condition}\`)\n`;
}
return '';
};
// Variable generators
c4aGenerator.forBlock['c4a_setvar'] = function(block, generator) {
const name = generator.getFieldValue(block, 'NAME');
const value = generator.getFieldValue(block, 'VALUE');
return `SETVAR ${name} = "${value}"\n`;
};
// Advanced generators
c4aGenerator.forBlock['c4a_eval'] = function(block, generator) {
const code = generator.getFieldValue(block, 'CODE');
return `EVAL \`${code}\`\n`;
};
c4aGenerator.forBlock['c4a_comment'] = function(block, generator) {
const text = generator.getFieldValue(block, 'TEXT');
return `# ${text}\n`;
};
// Procedure generators
c4aGenerator.forBlock['c4a_proc_def'] = function(block, generator) {
const name = generator.getFieldValue(block, 'NAME');
const body = generator.statementToCode(block, 'BODY');
return `PROC ${name}\n${body}ENDPROC\n`;
};
c4aGenerator.forBlock['c4a_proc_call'] = function(block, generator) {
const name = generator.getFieldValue(block, 'NAME');
return `${name}\n`;
};
// Override scrub_ to handle our custom format
c4aGenerator.scrub_ = function(block, code, opt_thisOnly) {
const nextBlock = block.nextConnection && block.nextConnection.targetBlock();
let nextCode = '';
if (nextBlock) {
if (!opt_thisOnly) {
nextCode = c4aGenerator.blockToCode(nextBlock);
// Add blank line between comment and non-comment blocks
const currentIsComment = block.type === 'c4a_comment';
const nextIsComment = nextBlock.type === 'c4a_comment';
// Add blank line when transitioning from command to comment or vice versa
if (currentIsComment !== nextIsComment && code.trim() && nextCode.trim()) {
nextCode = '\n' + nextCode;
}
}
}
return code + nextCode;
};