diff --git a/CodeCrafterAIX.txt b/CodeCrafterAIX.txt
new file mode 100644
index 0000000..5dfbfe4
--- /dev/null
+++ b/CodeCrafterAIX.txt
@@ -0,0 +1,5912 @@
+css/blockly.css:
+/* Estilo do Blockly e das abas */
+#blocklyDiv {
+ flex: 1;
+ overflow: hidden; /* Sem scrolls */
+}
+
+.tab-container {
+ width: 100%;
+ display: flex;
+ border-bottom: 1px solid #ccc;
+ flex-shrink: 0;
+}
+
+.tab-button {
+ padding: 10px 20px;
+ border: none;
+ background-color: #f1f1f1;
+ cursor: pointer;
+ outline: none;
+}
+
+.tab-button.active {
+ background-color: #ddd;
+ border-bottom: 2px solid #007bff;
+}
+
+.tab-content {
+ flex: 1;
+ display: none;
+ padding: 10px;
+ overflow: hidden;
+}
+
+.tab-content.active {
+ display: flex;
+ flex-direction: column;
+}
+
+.tab-content textarea, .tab-content pre {
+ flex: 1;
+ resize: none;
+ overflow: auto;
+ font-family: 'Fira Code', monospace;
+ font-size: 14px;
+ line-height: 1.5;
+ padding: 10px;
+ box-sizing: border-box;
+ border: 1px solid #ccc;
+ background-color: #f9f9f9;
+}
+
+
+css/global.css:
+/* Estilo global e layout */
+body {
+ margin: 0;
+ padding: 0;
+ font-family: Arial, sans-serif;
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ overflow: hidden; /* Remove scroll global */
+ background: #f0f0f0;
+}
+
+.main-content {
+ display: flex;
+ flex: 1;
+ position: relative;
+}
+
+.resize-handle {
+ width: 100%;
+ height: 5px;
+ background: #ccc;
+ cursor: ns-resize;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.resize-handle:hover {
+ background: #999;
+}
+
+
+css/header.css:
+/* Estilo do header */
+.header {
+ background: #2c3e50;
+ color: white;
+ padding: 10px 20px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-shrink: 0;
+}
+
+.header-controls {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+}
+
+.language-select, .theme-select {
+ background: white;
+ border: none;
+ padding: 6px;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+
+css/modals-notifications.css:
+/* Estilo de modais */
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+}
+
+.modal-content {
+ background: #fff;
+ padding: 20px;
+ border-radius: 8px;
+ text-align: center;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
+}
+
+.modal-content h2 {
+ margin: 0 0 10px;
+ font-size: 1.5em;
+}
+
+.modal-content p {
+ margin: 10px 0;
+}
+
+.download-link {
+ display: inline-block;
+ margin: 15px 0;
+ padding: 10px 15px;
+ background: #4caf50;
+ color: white;
+ text-decoration: none;
+ border-radius: 5px;
+}
+
+.download-link:hover {
+ background: #45a049;
+}
+
+/* Estilo de notificações */
+.notification {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ padding: 15px 25px;
+ border-radius: 5px;
+ color: white;
+ font-weight: bold;
+ z-index: 1000;
+ animation: slideIn 0.3s ease-out;
+}
+
+.notification.success {
+ background-color: #2ecc71;
+}
+
+.notification.error {
+ background-color: #e74c3c;
+}
+
+@keyframes slideIn {
+ from {
+ transform: translateX(100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+/* Estilo para o input de arquivo (oculto) */
+input[type="file"] {
+ display: none;
+}
+
+/* Melhorias nos botões de arquivo */
+.button.file-button {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.button.file-button span {
+ font-size: 18px;
+}
+
+.close-modal {
+ background: #f44336;
+ color: white;
+ padding: 10px 15px;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+.close-modal:hover {
+ background: #e53935;
+}
+
+.progress-circle {
+ margin: 20px auto;
+ width: 50px;
+ height: 50px;
+ border: 6px solid rgba(0, 0, 0, 0.2);
+ border-top: 6px solid #3498db;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+
+css/themes.css:
+/* Syntax highlighting */
+.language-xml {
+ height: 100vh;
+ width: 100%;
+ color: #d33682;
+}
+
+.language-yaml {
+ height: 100vh;
+ width: 100%;
+ color: #285b75;
+}
+
+.language-java {
+ height: 100vh;
+ width: 100%;
+ color: #2d2d2d;
+}
+
+.button {
+ background: #3498db;
+ color: white;
+ border: none;
+ padding: 8px 15px;
+ border-radius: 4px;
+ cursor: pointer;
+ margin-left: 10px;
+}
+
+.button:hover {
+ background: #2980b9;
+}
+
+/* Syntax highlighting */
+/* Seletor de tema */
+.theme-select {
+ background: white;
+ border: none;
+ padding: 6px;
+ border-radius: 4px;
+ cursor: pointer;
+ margin-right: 10px;
+}
+
+/* Tema Light (Padrão) */
+.theme-light {
+ --background-color: #ffffff;
+ --text-color: #24292e;
+ --category-text-color: #000000; /* Cor do texto das categorias */
+ --toolbox-background: #f1f1f1;
+ --blockly-background: #f9f9f9;
+}
+
+.theme-light .token.comment { color: #6a737d; }
+.theme-light .token.keyword { color: #d73a49; }
+.theme-light .token.string { color: #032f62; }
+.theme-light .token.class-name { color: #6f42c1; }
+.theme-light .token.function { color: #005cc5; }
+.theme-light .token.number { color: #005cc5; }
+.theme-light .token.operator { color: #d73a49; }
+.theme-light .token.punctuation { color: #24292e; }
+.theme-light .token.annotation { color: #d73a49; }
+
+/* Tema Dark */
+.theme-dark {
+ --background-color: #1e1e1e;
+ --text-color: #d4d4d4;
+ --category-text-color: #ffffff; /* Cor do texto das categorias */
+ --toolbox-background: #333333;
+ --blockly-background: #2e2e2e;
+}
+
+.theme-dark .token.comment { color: #6A9955; }
+.theme-dark .token.keyword { color: #569cd6; }
+.theme-dark .token.string { color: #ce9178; }
+.theme-dark .token.class-name { color: #4ec9b0; }
+.theme-dark .token.function { color: #dcdcaa; }
+.theme-dark .token.number { color: #b5cea8; }
+.theme-dark .token.operator { color: #d4d4d4; }
+.theme-dark .token.punctuation { color: #d4d4d4; }
+.theme-dark .token.annotation { color: #4fc1ff; }
+
+/* Tema GitHub */
+.theme-github {
+ --background-color: #ffffff;
+ --text-color: #24292e;
+ --category-text-color: #6a737d; /* Cor do texto das categorias */
+ --toolbox-background: #e1e4e8;
+ --blockly-background: #fafbfc;
+}
+
+.theme-github .token.comment { color: #6a737d; }
+.theme-github .token.keyword { color: #d73a49; }
+.theme-github .token.string { color: #032f62; }
+.theme-github .token.class-name { color: #6f42c1; }
+.theme-github .token.function { color: #005cc5; }
+.theme-github .token.number { color: #005cc5; }
+.theme-github .token.operator { color: #d73a49; }
+.theme-github .token.punctuation { color: #24292e; }
+.theme-github .token.annotation { color: #d73a49; }
+
+/* Tema Monokai */
+.theme-monokai {
+ --background-color: #272822;
+ --text-color: #f8f8f2;
+ --category-text-color: #a6e22e; /* Cor do texto das categorias */
+ --toolbox-background: #3e3d32;
+ --blockly-background: #49483e;
+}
+
+.theme-monokai .token.comment { color: #75715e; }
+.theme-monokai .token.keyword { color: #f92672; }
+.theme-monokai .token.string { color: #e6db74; }
+.theme-monokai .token.class-name { color: #a6e22e; }
+.theme-monokai .token.function { color: #66d9ef; }
+.theme-monokai .token.number { color: #ae81ff; }
+.theme-monokai .token.operator { color: #f8f8f2; }
+.theme-monokai .token.punctuation { color: #f8f8f2; }
+.theme-monokai .token.annotation { color: #f92672; }
+
+/* Estilos para o toolbox */
+#toolbox {
+ background-color: var(--toolbox-background);
+ color: var(--category-text-color);
+}
+
+/* Cores aplicadas aos componentes do Blockly */
+.blocklyToolboxDiv {
+ background-color: var(--toolbox-background) !important;
+}
+
+.blocklyTreeLabel {
+ color: var(--category-text-color) !important;
+}
+
+/*Circular progress*/
+.progress-circle {
+ margin: 20px auto;
+ width: 50px;
+ height: 50px;
+ border: 6px solid rgba(0, 0, 0, 0.2);
+ border-top: 6px solid #3498db;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+
+examples/mathoperations_blocks.json:
+{
+ "version": "1.0",
+ "timestamp": "2024-11-17T18:48:13.826Z",
+ "blocks": "\n \n com.bosonshiggs.mathoperations\n \n \n ANNOTATIONS\n \n \n com.google.appinventor.components.common.ComponentCategory\n FALSE\n FALSE\n \n \n com.google.appinventor.components.runtime.AndroidNonvisibleComponent\n FALSE\n FALSE\n \n \n com.google.appinventor.components.runtime.ComponentContainer\n FALSE\n FALSE\n \n \n com.google.appinventor.components.runtime.errors.YailRuntimeError\n FALSE\n FALSE\n \n \n com.google.appinventor.components.runtime.EventDispatcher\n FALSE\n FALSE\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Create Extension\n MathOperations\n Description\n An extension to perform basic and advanced mathematical operations.\n Category\n EXTENSION\n Icon Name\n TRUE\n Icon Name\n https://www.iconsdb.com/icons/preview/green/math-xxl.png\n Interfaces\n \n ANNOTATIONS\n BLOCKS\n \n \n \n MathOperations\n TRUE\n \n \n super(container.$form());\n \n \n \n \n \n \n \n \n Add\n double\n A custom method\n \n \n \n \n \n ADD\n \n \n @t#|;bxkmw45fYEBB`2i\n \n \n \n \n 2q?=^[QpR|dF(/XW?WIP\n \n \n \n \n \n \n \n \n \n \n \n \n Subtract\n double\n A custom method\n \n \n \n \n \n SUBTRACT\n \n \n @t#|;bxkmw45fYEBB`2i\n \n \n \n \n 2q?=^[QpR|dF(/XW?WIP\n \n \n \n \n \n \n \n \n \n \n \n \n Multiply\n double\n A custom method\n \n \n \n \n \n MULTIPLY\n \n \n @t#|;bxkmw45fYEBB`2i\n \n \n \n \n 2q?=^[QpR|dF(/XW?WIP\n \n \n \n \n \n \n \n \n \n \n \n \n Divide\n double\n A custom method\n \n \n \n \n EQ\n \n \n @t#|;bxkmw45fYEBB`2i\n \n \n \n \n 0\n \n \n \n \n \n \n message\n String\n FALSE\n \n \n ena,|sXYve9w}{nA^xV/\n \n \n Error! Division by zero!\n \n \n \n \n \n \n \n Error\n \n \n \n \n 0\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n DIVIDE\n \n \n @t#|;bxkmw45fYEBB`2i\n \n \n \n \n 2q?=^[QpR|dF(/XW?WIP\n \n \n \n \n \n \n \n \n \n \n \n \n \n Error\n A custom event\n \n \n \n \n \n Dispatcher\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n"
+}
+
+examples/MathOperations.java:
+package com.bosonshiggs.mathoperations;
+
+import com.google.appinventor.components.annotations.*;
+import com.google.appinventor.components.common.ComponentCategory;
+import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
+import com.google.appinventor.components.runtime.ComponentContainer;
+import com.google.appinventor.components.runtime.errors.YailRuntimeError;
+
+@DesignerComponent(version = 1,
+ description = "An extension to perform basic and advanced mathematical operations.",
+ category = ComponentCategory.EXTENSION,
+ nonVisible = true,
+ iconName = "https://www.iconsdb.com/icons/preview/green/math-xxl.png")
+@SimpleObject(external = true)
+public class MathOperations extends AndroidNonvisibleComponent {
+ public Constructor(ComponentContainer container) {
+
+ super(container.$context());
+ }
+ @SimpleFunction(description = "A custom method")
+ public double Add(String number1, String number2) {
+ return (message + message);
+
+ }
+ @SimpleFunction(description = "A custom method")
+ public double Subtract(String number1, String number2) {
+ return (message - message);
+
+ }
+ @SimpleFunction(description = "A custom method")
+ public double Multiply(String number1, String number2) {
+ return (message * message);
+
+ }
+ @SimpleFunction(description = "A custom method")
+ public double Divide(String number1, String number2) {
+ if (message.equals(0)) {
+ String message;
+ message = "Error! Division by zero!";
+ Error(message);
+
+ } else {
+ return (message / message);
+
+ }
+
+ }
+ @SimpleEvent(description = "A custom event")
+ public void Error(String message) {
+ EventDispatcher.dispatchEvent(this, "Dispatcher", message);
+
+ }
+}
+
+index.html:
+
+
+
+
+
+
+ CodeCrafter AIX
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* Output Java code will appear here */
+
+
+
+
+
Add Dependencies (JAR/AAR)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ java.util.HashMap
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+js/blocks/blocks.js:
+// Define custom blocks
+
+
+
+Blockly.Blocks['import_basic'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Import")
+ .appendField(new Blockly.FieldDropdown([
+ ["com.google.appinventor.components.annotations.*", "ANNOTATIONS"],
+ ["com.google.appinventor.components.common.*", "COMMON"],
+ ["com.google.appinventor.components.runtime.*", "RUNTIME"],
+ ["android.content.Context", "CONTEXT"]
+ ]), "IMPORT");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour(210);
+ this.setTooltip("Imports necessary libraries");
+ this.setHelpUrl("");
+ }
+};
+
+// Import Static Method
+
+Blockly.Blocks['designer_property'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("designerProperty"), "PROPERTY_NAME")
+ .appendField("type")
+ .appendField(new Blockly.FieldDropdown([
+ ["string", "PropertyTypeConstants.PROPERTY_TYPE_STRING"],
+ ["boolean", "PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN"],
+ ["integer", "PropertyTypeConstants.PROPERTY_TYPE_INTEGER"],
+ ["color", "PropertyTypeConstants.PROPERTY_TYPE_COLOR"]
+ ]), "TYPE");
+ this.appendDummyInput()
+ .appendField("Description")
+ .appendField(new Blockly.FieldTextInput("A designer property"), "DESCRIPTION");
+ this.appendDummyInput()
+ .appendField("Category")
+ .appendField(new Blockly.FieldDropdown([
+ ["BEHAVIOR", "PropertyCategory.BEHAVIOR"],
+ ["APPEARANCE", "PropertyCategory.APPEARANCE"]
+ ]), "CATEGORY");
+ this.appendStatementInput("DESIGNER_PROPERTY_SETTER")
+ .setCheck(null)
+ .appendField("Setter");
+ this.appendStatementInput("DESIGNER_PROPERTY_GETTER")
+ .setCheck(null)
+ .appendField("Getter");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour(160);
+ this.setTooltip("Declares a designer property");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.Blocks['property_declaration'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("myProperty"), "PROPERTY_NAME")
+ .appendField("type")
+ .appendField(new Blockly.FieldDropdown([
+ ["String", "String"],
+ ["int", "int"],
+ ["boolean", "boolean"],
+ ["double", "double"]
+ ]), "TYPE");
+ this.appendStatementInput("PROPERTY_SETTER")
+ .setCheck(null)
+ .appendField("Setter");
+ this.appendStatementInput("PROPERTY_GETTER")
+ .setCheck(null)
+ .appendField("Getter");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour(160);
+ this.setTooltip("Declares a property");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.Blocks['set_property'] = {
+ init: function() {
+ this.appendValueInput("VALUE")
+ .setCheck(null)
+ .appendField("set")
+ .appendField(new Blockly.FieldTextInput("myProperty"), "PROPERTY");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour(290);
+ this.setTooltip("Set property value");
+ this.setHelpUrl("");
+ }
+};
+
+
+Blockly.Blocks['get_property'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("get")
+ .appendField(new Blockly.FieldTextInput("myProperty"), "PROPERTY");
+ this.setOutput(true, null);
+ this.setColour(290);
+ this.setTooltip("Get property value");
+ this.setHelpUrl("");
+ }
+};
+
+
+
+
+//Generators
+// Java code generator
+Blockly.JavaScript['set_property'] = function(block) {
+ let property = block.getFieldValue('PROPERTY');
+ let value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC);
+ let code = `set${property.charAt(0).toUpperCase() + property.slice(1)}(${value});\n`;
+ return code;
+};
+
+Blockly.JavaScript['get_property'] = function(block) {
+ let property = block.getFieldValue('PROPERTY');
+ let code = `get${property.charAt(0).toUpperCase() + property.slice(1)}()`;
+ return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
+};
+
+Blockly.JavaScript['property_declaration'] = function(block) {
+ let propertyName = block.getFieldValue('PROPERTY_NAME');
+ let type = block.getFieldValue('TYPE');
+ let setterContent = Blockly.JavaScript.statementToCode(block, 'PROPERTY_SETTER');
+ let getterContent = Blockly.JavaScript.statementToCode(block, 'PROPERTY_GETTER');
+
+ return ` private ${type} ${propertyName};
+
+ public void set${propertyName.charAt(0).toUpperCase() + propertyName.slice(1)}(${type} value) {
+ ${setterContent}
+ }
+
+ public ${type} get${propertyName.charAt(0).toUpperCase() + propertyName.slice(1)}() {
+ ${getterContent}
+ }\n`;
+};
+
+Blockly.JavaScript['designer_property'] = function(block) {
+ let propertyName = block.getFieldValue('PROPERTY_NAME');
+ let type = block.getFieldValue('TYPE');
+ let description = block.getFieldValue('DESCRIPTION');
+ let category = block.getFieldValue('CATEGORY');
+ let setterContent = Blockly.JavaScript.statementToCode(block, 'DESIGNER_PROPERTY_SETTER');
+ let getterContent = Blockly.JavaScript.statementToCode(block, 'DESIGNER_PROPERTY_GETTER');
+
+ return ` @DesignerProperty(editorType = ${type}, defaultValue = "")
+ @SimpleProperty(description = "${description}")
+ public void ${propertyName}(String value) {
+ ${setterContent}
+ }
+
+ @SimpleProperty(category = ${category})
+ public String ${propertyName}() {
+ ${getterContent}
+ }\n`;
+};
+
+Blockly.JavaScript['import_basic'] = function(block) {
+ let importType = block.getFieldValue('IMPORT');
+ let imports = {
+ 'ANNOTATIONS': 'com.google.appinventor.components.annotations.*',
+ 'COMMON': 'com.google.appinventor.components.common.*',
+ 'RUNTIME': 'com.google.appinventor.components.runtime.*',
+ 'CONTEXT': 'android.content.Context'
+ };
+ return `import ${imports[importType]};\n`;
+};
+
+
+
+
+
+js/blocks/color_blocks.js:
+//blocks/color_blocks.js
+'use strict';
+
+// Color Picker
+Blockly.Blocks['color_picker'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('color')
+ .appendField(new Blockly.FieldColour('#ff0000'), 'COLOR');
+ this.setOutput(true, 'Color');
+ this.setColour("#D81B60");
+ this.setTooltip('Selects a color');
+ }
+};
+
+// RGB Color
+Blockly.Blocks['color_rgb'] = {
+ init: function() {
+ this.appendValueInput('RED')
+ .setCheck('Number')
+ .appendField('color with R');
+ this.appendValueInput('GREEN')
+ .setCheck('Number')
+ .appendField('G');
+ this.appendValueInput('BLUE')
+ .setCheck('Number')
+ .appendField('B');
+ this.setOutput(true, 'Color');
+ this.setColour("#D81B60");
+ this.setTooltip('Creates a color from RGB values (0-255)');
+ }
+};
+
+// Blend Colors
+Blockly.Blocks['color_blend'] = {
+ init: function() {
+ this.appendValueInput('COLOR1')
+ .setCheck('Color')
+ .appendField('blend color');
+ this.appendValueInput('COLOR2')
+ .setCheck('Color')
+ .appendField('with');
+ this.appendValueInput('RATIO')
+ .setCheck('Number')
+ .appendField('ratio');
+ this.setOutput(true, 'Color');
+ this.setColour("#D81B60");
+ this.setTooltip('Blends two colors with a ratio (0-1)');
+ }
+};
+
+// Random Color
+Blockly.Blocks['color_random'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('random color');
+ this.setOutput(true, 'Color');
+ this.setColour("#D81B60");
+ this.setTooltip('Generates a random color');
+ }
+};
+
+// Adjust Brightness
+Blockly.Blocks['color_brightness'] = {
+ init: function() {
+ this.appendValueInput('COLOR')
+ .setCheck('Color')
+ .appendField('adjust brightness of');
+ this.appendValueInput('AMOUNT')
+ .setCheck('Number')
+ .appendField('by amount');
+ this.setOutput(true, 'Color');
+ this.setColour("#D81B60");
+ this.setTooltip('Adjusts the brightness of a color by a specified amount');
+ }
+};
+
+// Convert HEX to RGB
+Blockly.Blocks['color_hex_to_rgb'] = {
+ init: function() {
+ this.appendValueInput('HEX')
+ .setCheck('String')
+ .appendField('convert HEX');
+ this.setOutput(true, 'Color');
+ this.setColour("#D81B60");
+ this.setTooltip('Converts a HEX color string to an RGB color');
+ }
+};
+
+// Convert RGB to HEX
+Blockly.Blocks['color_rgb_to_hex'] = {
+ init: function() {
+ this.appendValueInput('COLOR')
+ .setCheck('Color')
+ .appendField('convert to HEX');
+ this.setOutput(true, 'String');
+ this.setColour("#D81B60");
+ this.setTooltip('Converts an RGB color to a HEX color string');
+ }
+};
+
+//Generators
+// Code Generator for Color Picker
+Blockly.JavaScript['color_picker'] = function(block) {
+ // Get the selected color value from the block (e.g., '#ff0000')
+ let color = block.getFieldValue('COLOR');
+
+ // Remove the '#' character and convert to uppercase (e.g., 'FF0000')
+ let hex = color.substring(1).toUpperCase();
+
+ // Prepend 'FF' for the alpha channel to make it ARGB (e.g., 'FFFF0000')
+ let argbHex = 'FF' + hex;
+
+ // Generate JavaScript code to parse the ARGB hex string into a signed 32-bit integer
+ // The bitwise OR with 0 (`| 0`) converts the number to a signed 32-bit integer
+ let code = `parseInt('${argbHex}', 16) | 0`;
+
+ // Return the generated code and specify the order of operations
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+
+// Code Generator for RGB Color
+// Code Generator for RGB Color
+Blockly.JavaScript['color_rgb'] = function(block) {
+ let red = Blockly.JavaScript.valueToCode(block, 'RED', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ let green = Blockly.JavaScript.valueToCode(block, 'GREEN', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ let blue = Blockly.JavaScript.valueToCode(block, 'BLUE', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+
+ // Generate Java code using Color.rgb method
+ let code = `Color.rgb(${red}, ${green}, ${blue})`;
+
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+
+// Code Generator for Blend Colors
+// Code Generator for Blend Colors
+Blockly.JavaScript['color_blend'] = function(block) {
+ let color1 = Blockly.JavaScript.valueToCode(block, 'COLOR1', Blockly.JavaScript.ORDER_ATOMIC) || 'Color.BLACK';
+ let color2 = Blockly.JavaScript.valueToCode(block, 'COLOR2', Blockly.JavaScript.ORDER_ATOMIC) || 'Color.WHITE';
+ let ratio = Blockly.JavaScript.valueToCode(block, 'RATIO', Blockly.JavaScript.ORDER_ATOMIC) || '0.5';
+
+ // Generate Java code using a helper method to blend colors
+ // Since we cannot define helper methods within generated code, we'll inline the blending logic
+ let code = `(int) ((1 - ${ratio}) * ${color1} + ${ratio} * ${color2})`;
+
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+// Code Generator for Random Color
+Blockly.JavaScript['color_random'] = function(block) {
+ // Generate Java code to create a random color using Math.random()
+ let code = `(int)(Math.random() * 0x1000000) | 0xFF000000`;
+
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+// Code Generator for Adjust Brightness
+// Code Generator for Adjust Brightness
+Blockly.JavaScript['color_brightness'] = function(block) {
+ let color = Blockly.JavaScript.valueToCode(block, 'COLOR', Blockly.JavaScript.ORDER_ATOMIC) || 'Color.BLACK';
+ let amount = Blockly.JavaScript.valueToCode(block, 'AMOUNT', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+
+ // Generate Java code to adjust brightness
+ // This involves extracting RGB components, adjusting them, and recombining
+ let code = `Color.argb(Color.alpha(${color}),
+ Math.min(255, Math.max(0, Color.red(${color}) + ${amount})),
+ Math.min(255, Math.max(0, Color.green(${color}) + ${amount})),
+ Math.min(255, Math.max(0, Color.blue(${color}) + ${amount})))`;
+
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+// Code Generator for Convert HEX to RGB
+// Code Generator for Convert HEX to RGB
+Blockly.JavaScript['color_hex_to_rgb'] = function(block) {
+ let hex = Blockly.JavaScript.valueToCode(block, 'HEX', Blockly.JavaScript.ORDER_ATOMIC) || '"#000000"';
+
+ // Generate Java code to convert HEX to RGB string
+ let code = `Color.red(Color.parseColor(${hex})) + ", " + Color.green(Color.parseColor(${hex})) + ", " + Color.blue(Color.parseColor(${hex}))`;
+
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+// Code Generator for Convert RGB to HEX
+// Code Generator for Convert RGB to HEX
+Blockly.JavaScript['color_rgb_to_hex'] = function(block) {
+ let rgb = Blockly.JavaScript.valueToCode(block, 'COLOR', Blockly.JavaScript.ORDER_ATOMIC) || '"rgb(0, 0, 0)"';
+
+ // Generate Java code to convert RGB string to HEX color
+ let code = `String.format("#%02X%02X%02X",
+ Integer.parseInt(${rgb}.split(",")[0].replace("rgb(", "").trim()),
+ Integer.parseInt(${rgb}.split(",")[1].trim()),
+ Integer.parseInt(${rgb}.split(",")[2].replace(")", "").trim()))`;
+
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+
+js/blocks/comments_blocks.js:
+'use strict';
+
+/**
+ * Define comment blocks for Blockly.
+ */
+
+/**
+ * Single-Line Comment Block
+ */
+Blockly.Blocks['comment_single'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('//')
+ .appendField(new Blockly.FieldTextInput('Your comment here'), 'COMMENT_TEXT');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour(210); // Hue for comments (grey)
+ this.setTooltip('Inserts a single-line comment.');
+ this.setHelpUrl('https://docs.oracle.com/javase/tutorial/java/nutsandbolts/comments.html');
+ }
+};
+
+Blockly.Blocks['comment_multi_text'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('')
+ .appendField(new Blockly.FieldTextInput('Your comment here'), 'COMMENT_TEXT');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour(210); // Hue for comments (grey)
+ this.setTooltip('Inserts a single-line comment.');
+ this.setHelpUrl('https://docs.oracle.com/javase/tutorial/java/nutsandbolts/comments.html');
+ }
+};
+
+/**
+ * Multi-Line Comment Block
+ */
+Blockly.Blocks['comment_multi'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('/*');
+ this.appendStatementInput('COMMENT_LINES')
+ .setCheck('comment_line');
+ this.appendDummyInput()
+ .appendField('*/');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour(210); // Hue for comments (grey)
+ this.setTooltip('Inserts a multi-line comment. Add comment lines below.');
+ this.setHelpUrl('https://docs.oracle.com/javase/tutorial/java/nutsandbolts/comments.html');
+ }
+};
+
+/**
+ * JavaScript Code Generator for Single-Line Comment
+ */
+Blockly.JavaScript['comment_single'] = function(block) {
+ const commentText = block.getFieldValue('COMMENT_TEXT');
+ const escapedText = commentText.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
+ return `// ${escapedText}\n`;
+};
+
+/**
+ * JavaScript Code Generator for Multi-Line Comment
+ */
+Blockly.JavaScript['comment_multi'] = function(block) {
+ const commentLines = Blockly.JavaScript.statementToCode(block, 'COMMENT_LINES').trim().split('\n');
+ const formattedLines = commentLines.map(line => ` * ${line.trim()}`).join('\n');
+ return `/*\n${formattedLines}\n */\n`;
+};
+
+/**
+ * JavaScript Code Generator for comment_multi_text
+ */
+Blockly.JavaScript['comment_multi_text'] = function(block) {
+ const commentText = block.getFieldValue('COMMENT_TEXT');
+ const escapedText = commentText.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
+ return ` ${escapedText}\n`;
+};
+
+js/blocks/configurations.js:
+
+
+js/blocks/constructor_blocks.js:
+//blocks/constructor_blocks.js
+
+//Blocks
+Blockly.Blocks['extension_class'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldLabelSerializable("Create Extension"), "className")
+ .appendField(new Blockly.FieldTextInput("MyExtension"), "classNameEdit");
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldLabelSerializable("Description"), "descriptionName")
+ .appendField(new Blockly.FieldTextInput("A custom extension"), "descriptionNameEdit");
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldLabelSerializable("Category"), "categoryName")
+ .appendField(new Blockly.FieldTextInput("EXTENSION"), "extensionNameEdit");
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldLabelSerializable("Non-visible"), "NAME")
+ .appendField(new Blockly.FieldCheckbox("TRUE"), "checkBoxNonVisible");
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldLabelSerializable("Icon Name"), "NAME")
+ .appendField(new Blockly.FieldTextInput("image/icon.png"), "imageURL");
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldLabelSerializable("Interfaces"), "interfacesLabel")
+ .appendField(new Blockly.FieldTextInput(""), "interfaces");
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldLabelSerializable("ANNOTATIONS"), "annotationsText");
+ this.appendStatementInput("annotationBlocks")
+ .setCheck(null);
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldLabelSerializable("BLOCKS"), "blocksExtensions");
+ this.appendStatementInput("statementBlocks")
+ .setCheck(null);
+ this.setPreviousStatement(true, null);
+ this.setColour("#5C6BC0");
+ this.setTooltip("");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.Blocks['package_declaration'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Package")
+ .appendField(new Blockly.FieldTextInput("com.example.extension"), "PACKAGE");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#5C6BC0");
+ this.setTooltip("Declares the extension package");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.Blocks['custom_annotation'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Custom Annotation")
+ .appendField(new Blockly.FieldTextInput("@MyAnnotation"), "ANNOTATION");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#5C6BC0");
+ this.setTooltip("Adds a custom annotation above @SimpleObject");
+ this.setHelpUrl("");
+ }
+};
+
+// Bloco do construtor
+Blockly.Blocks['constructor_declaration'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput('Constructor'), 'CONSTRUCTOR_NAME')
+ .appendField(new Blockly.FieldCheckbox("TRUE"), "IS_DEFAULT")
+ .appendField("default");
+ this.appendStatementInput('PARAMETERS')
+ .setCheck('constructor_parameter')
+ .appendField('parameters');
+ this.appendStatementInput('SUPER_CALL')
+ .setCheck('constructor_super_call')
+ .appendField('super call');
+ this.appendStatementInput('CONSTRUCTOR_CONTENT')
+ .setCheck(null)
+ .appendField('implementation');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#5C6BC0");
+ this.setTooltip('Define o construtor da extensão');
+
+ this.updateShape_();
+ },
+
+ mutationToDom: function() {
+ var container = document.createElement('mutation');
+ var isDefault = (this.getFieldValue('IS_DEFAULT') == 'TRUE');
+ container.setAttribute('is_default', isDefault);
+ return container;
+ },
+
+ domToMutation: function(xmlElement) {
+ var isDefault = (xmlElement.getAttribute('is_default') == 'true');
+ this.getField('IS_DEFAULT').setValue(isDefault ? 'TRUE' : 'FALSE');
+ this.updateShape_();
+ },
+
+ updateShape_: function() {
+ var isDefault = (this.getFieldValue('IS_DEFAULT') == 'TRUE');
+ if (isDefault) {
+ if (this.getInput('PARAMETERS')) this.removeInput('PARAMETERS');
+ if (this.getInput('SUPER_CALL')) this.removeInput('SUPER_CALL');
+ } else {
+ if (!this.getInput('PARAMETERS')) {
+ this.appendStatementInput('PARAMETERS')
+ .setCheck('constructor_parameter')
+ .appendField('parameters');
+ }
+ if (!this.getInput('SUPER_CALL')) {
+ this.appendStatementInput('SUPER_CALL')
+ .setCheck('constructor_super_call')
+ .appendField('super call');
+ }
+ }
+ }
+};
+
+// Bloco de parâmetro do construtor
+Blockly.Blocks['constructor_parameter'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('parameter')
+ .appendField(new Blockly.FieldTextInput('paramName'), 'PARAM_NAME')
+ .appendField('type')
+ .appendField(new Blockly.FieldDropdown([
+ ['ComponentContainer', 'ComponentContainer'],
+ ['Context', 'Context'],
+ ['String', 'String'],
+ ['int', 'int'],
+ ['boolean', 'boolean']
+ ]), 'PARAM_TYPE');
+ this.setPreviousStatement(true, 'constructor_parameter');
+ this.setNextStatement(true, 'constructor_parameter');
+ this.setColour("#5C6BC0");
+ this.setTooltip('Adiciona um parâmetro ao construtor');
+ }
+};
+
+// Bloco para chamada super
+Blockly.Blocks['constructor_super_call'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("super(container.$context())"), "SUPER_PARAMS");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour(230);
+ this.setTooltip("");
+ this.setHelpUrl("");
+ }
+};
+
+// Geradores de código
+Blockly.JavaScript['constructor_declaration'] = function(block) {
+ var constructorName = block.getFieldValue('CONSTRUCTOR_NAME');
+ var isDefault = block.getFieldValue('IS_DEFAULT') == 'TRUE';
+ var content = Blockly.JavaScript.statementToCode(block, 'CONSTRUCTOR_CONTENT') || '';
+
+ if (isDefault) {
+ return ` public ${constructorName}(ComponentContainer container) {
+${content} }\n`;
+ } else {
+ var parameters = Blockly.JavaScript.statementToCode(block, 'PARAMETERS') || '';
+ var superCall = Blockly.JavaScript.statementToCode(block, 'SUPER_CALL') || '';
+
+ parameters = parameters.trim().replace(/,\s*$/, '');
+
+ return ` public ${constructorName}(${parameters}) {
+${superCall}${content} }\n`;
+ }
+};
+
+Blockly.JavaScript['constructor_parameter'] = function(block) {
+ var paramName = block.getFieldValue('PARAM_NAME');
+ var paramType = block.getFieldValue('PARAM_TYPE');
+
+ return paramType + ' ' + paramName + ', ';
+};
+
+Blockly.JavaScript['constructor_super_call'] = function(block) {
+ var params = block.getFieldValue('SUPER_PARAMS');
+ var afterSuper = Blockly.JavaScript.statementToCode(block, 'AFTER_SUPER') || '';
+
+ return ` ${params};
+${afterSuper}`;
+};
+
+//Generators
+Blockly.JavaScript['extension_class'] = function(block) {
+ let interfaces = block.getFieldValue('interfaces');
+ let className = block.getFieldValue('classNameEdit');
+ let description = block.getFieldValue('descriptionNameEdit');
+ let category = block.getFieldValue('extensionNameEdit');
+ let nonVisible = block.getFieldValue('checkBoxNonVisible') === 'TRUE';
+ let iconName = block.getFieldValue('imageURL');
+ let annotations = Blockly.JavaScript.statementToCode(block, 'annotationBlocks');
+ let members = Blockly.JavaScript.statementToCode(block, 'statementBlocks');
+
+ let code = `@DesignerComponent(version = 1,
+ description = "${description}",
+ category = ComponentCategory.${category},
+ nonVisible = ${nonVisible},
+ iconName = "${iconName}")
+@SimpleObject(external = true)
+${annotations}public class ${className} extends AndroidNonvisibleComponent${interfaces ? ' implements ' + interfaces : ''} {
+${members}}`;
+
+ return code;
+};
+
+Blockly.JavaScript['custom_annotation'] = function(block) {
+ let annotation = block.getFieldValue('ANNOTATION');
+ return `${annotation}\n`;
+};
+
+
+Blockly.JavaScript['package_declaration'] = function(block) {
+ let package = block.getFieldValue('PACKAGE');
+ return `package ${package};\n\n`;
+};
+
+js/blocks/control_blocks.js:
+// Bloco If-Else
+Blockly.Blocks['controls_if_java'] = {
+ init: function() {
+ this.appendValueInput('IF0')
+ .setCheck('Boolean')
+ .appendField('if');
+ this.appendStatementInput('DO0')
+ .appendField('do');
+ this.appendStatementInput('ELSE')
+ .appendField('else');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#E53935");
+ this.setTooltip('If-else control statement');
+ }
+};
+
+// Bloco For
+Blockly.Blocks['controls_for_java'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('for')
+ .appendField(new Blockly.FieldTextInput('i'), 'VAR')
+ .appendField('from')
+ .appendField(new Blockly.FieldNumber(0), 'FROM')
+ .appendField('to')
+ .appendField(new Blockly.FieldNumber(10), 'TO')
+ .appendField('step')
+ .appendField(new Blockly.FieldNumber(1), 'STEP');
+ this.appendStatementInput('DO')
+ .appendField('do');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#E53935");
+ this.setTooltip('For loop with variable');
+ }
+};
+
+// Bloco While
+Blockly.Blocks['controls_while_java'] = {
+ init: function() {
+ this.appendValueInput('CONDITION')
+ .setCheck('Boolean')
+ .appendField('while');
+ this.appendStatementInput('DO')
+ .appendField('do');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#E53935");
+ this.setTooltip('While loop');
+ }
+};
+
+// Geradores de código para os blocos de controle
+Blockly.JavaScript['controls_if_java'] = function(block) {
+ let condition = Blockly.JavaScript.valueToCode(block, 'IF0', Blockly.JavaScript.ORDER_NONE) || 'false';
+ let thenBranch = Blockly.JavaScript.statementToCode(block, 'DO0');
+ let elseBranch = Blockly.JavaScript.statementToCode(block, 'ELSE');
+
+ return `if (${condition}) {
+ ${thenBranch}
+ } else {
+ ${elseBranch}
+ }\n`;
+};
+
+Blockly.JavaScript['controls_for_java'] = function(block) {
+ let variable = block.getFieldValue('VAR');
+ let from = block.getFieldValue('FROM');
+ let to = block.getFieldValue('TO');
+ let step = block.getFieldValue('STEP');
+ let branch = Blockly.JavaScript.statementToCode(block, 'DO');
+
+ return `for (int ${variable} = ${from}; ${variable} <= ${to}; ${variable} += ${step}) {
+ ${branch}
+ }\n`;
+};
+
+Blockly.JavaScript['controls_while_java'] = function(block) {
+ let condition = Blockly.JavaScript.valueToCode(block, 'CONDITION', Blockly.JavaScript.ORDER_NONE) || 'false';
+ let branch = Blockly.JavaScript.statementToCode(block, 'DO');
+
+ return `while (${condition}) {
+ ${branch}
+ }\n`;
+};
+
+// Bloco If-Else
+Blockly.Blocks['controls_if_java'] = {
+ init: function() {
+ this.appendValueInput('IF0')
+ .setCheck('Boolean')
+ .appendField('if');
+ this.appendStatementInput('DO0')
+ .appendField('do');
+ this.appendStatementInput('ELSE')
+ .appendField('else');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#E53935");
+ this.setTooltip('If-else control statement');
+ }
+};
+
+// Bloco For
+Blockly.Blocks['controls_for_java'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('for')
+ .appendField(new Blockly.FieldTextInput('i'), 'VAR')
+ .appendField('from')
+ .appendField(new Blockly.FieldNumber(0), 'FROM')
+ .appendField('to')
+ .appendField(new Blockly.FieldNumber(10), 'TO')
+ .appendField('step')
+ .appendField(new Blockly.FieldNumber(1), 'STEP');
+ this.appendStatementInput('DO')
+ .appendField('do');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#E53935");
+ this.setTooltip('For loop with variable');
+ }
+};
+
+// Bloco While
+Blockly.Blocks['controls_while_java'] = {
+ init: function() {
+ this.appendValueInput('CONDITION')
+ .setCheck('Boolean')
+ .appendField('while');
+ this.appendStatementInput('DO')
+ .appendField('do');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#E53935");
+ this.setTooltip('While loop');
+ }
+};
+
+// Try-Catch Block
+Blockly.Blocks['controls_try_catch'] = {
+ init: function() {
+ this.appendStatementInput('TRY')
+ .setCheck(null)
+ .appendField('try');
+ this.appendStatementInput('CATCH')
+ .setCheck(null)
+ .appendField('catch')
+ .appendField(new Blockly.FieldTextInput('Exception'), 'EXCEPTION_TYPE')
+ .appendField(new Blockly.FieldTextInput('e'), 'EXCEPTION_VAR');
+ this.appendStatementInput('FINALLY')
+ .setCheck(null)
+ .appendField('finally');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#E53935");
+ this.setTooltip('Try-Catch-Finally block');
+ }
+};
+
+// Return Block
+Blockly.Blocks['controls_return'] = {
+ init: function() {
+ this.appendValueInput('VALUE')
+ .setCheck(null)
+ .appendField('return');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(false, null); // Return geralmente é a última declaração
+ this.setColour("#E53935");
+ this.setTooltip('Return statement');
+ }
+};
+
+// Geradores de código para os blocos de controle
+Blockly.JavaScript['controls_if_java'] = function(block) {
+ let condition = Blockly.JavaScript.valueToCode(block, 'IF0', Blockly.JavaScript.ORDER_NONE) || 'false';
+ let thenBranch = Blockly.JavaScript.statementToCode(block, 'DO0');
+ let elseBranch = Blockly.JavaScript.statementToCode(block, 'ELSE');
+
+ return `if (${condition}) {
+ ${thenBranch}
+ } else {
+ ${elseBranch}
+ }\n`;
+};
+
+Blockly.JavaScript['controls_for_java'] = function(block) {
+ let variable = block.getFieldValue('VAR');
+ let from = block.getFieldValue('FROM');
+ let to = block.getFieldValue('TO');
+ let step = block.getFieldValue('STEP');
+ let branch = Blockly.JavaScript.statementToCode(block, 'DO');
+
+ return `for (int ${variable} = ${from}; ${variable} <= ${to}; ${variable} += ${step}) {
+ ${branch}
+ }\n`;
+};
+
+Blockly.JavaScript['controls_while_java'] = function(block) {
+ let condition = Blockly.JavaScript.valueToCode(block, 'CONDITION', Blockly.JavaScript.ORDER_NONE) || 'false';
+ let branch = Blockly.JavaScript.statementToCode(block, 'DO');
+
+ return `while (${condition}) {
+ ${branch}
+ }\n`;
+};
+
+Blockly.JavaScript['controls_try_catch'] = function(block) {
+ let tryCode = Blockly.JavaScript.statementToCode(block, 'TRY') || '';
+ let catchCode = Blockly.JavaScript.statementToCode(block, 'CATCH') || '';
+ let finallyCode = Blockly.JavaScript.statementToCode(block, 'FINALLY') || '';
+ let exceptionType = block.getFieldValue('EXCEPTION_TYPE');
+ let exceptionlet = block.getFieldValue('EXCEPTION_VAR');
+
+ let code = 'try {\n' +
+ tryCode +
+ '} catch (' + exceptionType + ' ' + exceptionlet + ') {\n' +
+ catchCode;
+
+ if (finallyCode) {
+ code += '} finally {\n' + finallyCode;
+ }
+
+ return code + '}\n';
+};
+
+Blockly.JavaScript['controls_return'] = function(block) {
+ let value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC) || '';
+ return 'return ' + value + ';\n';
+};
+
+js/blocks/dictionary_blocks.js:
+//blocks/dictionary_blocks.js
+'use strict';
+
+// Create a new HashMap
+Blockly.Blocks['dict_create'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('new HashMap<')
+ .appendField(new Blockly.FieldDropdown([
+ ['String', 'String'],
+ ['Integer', 'Integer'],
+ ['Boolean', 'Boolean'],
+ ['Object', 'Object']
+ ]), 'KEY_TYPE')
+ .appendField(',')
+ .appendField(new Blockly.FieldDropdown([
+ ['String', 'String'],
+ ['Integer', 'Integer'],
+ ['Boolean', 'Boolean'],
+ ['Object', 'Object']
+ ]), 'VALUE_TYPE')
+ .appendField('>');
+ this.setOutput(true, 'HashMap');
+ this.setColour("#43A047");
+ this.setTooltip('Creates a new HashMap');
+ }
+};
+
+// Get value from HashMap
+Blockly.Blocks['dict_get'] = {
+ init: function() {
+ this.appendValueInput('DICT')
+ .setCheck('HashMap')
+ .appendField('get value from');
+ this.appendValueInput('KEY')
+ .setCheck(null)
+ .appendField('with key');
+ this.setOutput(true, null);
+ this.setColour("#43A047");
+ this.setTooltip('Gets a value from the HashMap by key');
+ }
+};
+
+// Set value in HashMap
+Blockly.Blocks['dict_set'] = {
+ init: function() {
+ this.appendValueInput('DICT')
+ .setCheck('HashMap')
+ .appendField('set in');
+ this.appendValueInput('KEY')
+ .setCheck(null)
+ .appendField('key');
+ this.appendValueInput('VALUE')
+ .setCheck(null)
+ .appendField('value');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#43A047");
+ this.setTooltip('Sets a value in the HashMap by key');
+ }
+};
+
+// Remove value from HashMap
+Blockly.Blocks['dict_remove'] = {
+ init: function() {
+ this.appendValueInput('DICT')
+ .setCheck('HashMap')
+ .appendField('remove from');
+ this.appendValueInput('KEY')
+ .setCheck(null)
+ .appendField('key');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#43A047");
+ this.setTooltip('Removes a value from the HashMap by key');
+ }
+};
+
+// Check if HashMap contains key
+Blockly.Blocks['dict_containsKey'] = {
+ init: function() {
+ this.appendValueInput('DICT')
+ .setCheck('HashMap')
+ .appendField('contains key');
+ this.appendValueInput('KEY')
+ .setCheck(null);
+ this.setOutput(true, 'Boolean');
+ this.setColour("#43A047");
+ this.setTooltip('Checks if the HashMap contains the specified key');
+ }
+};
+
+// Get all keys
+Blockly.Blocks['dict_keys'] = {
+ init: function() {
+ this.appendValueInput('DICT')
+ .setCheck('HashMap')
+ .appendField('get all keys from');
+ this.setOutput(true, 'Set');
+ this.setColour("#43A047");
+ this.setTooltip('Gets all keys from the HashMap');
+ }
+};
+
+// Get all values
+Blockly.Blocks['dict_values'] = {
+ init: function() {
+ this.appendValueInput('DICT')
+ .setCheck('HashMap')
+ .appendField('get all values from');
+ this.setOutput(true, 'Collection');
+ this.setColour("#43A047");
+ this.setTooltip('Gets all values from the HashMap');
+ }
+};
+
+// Code Generators
+Blockly.JavaScript['dict_create'] = function(block) {
+ const keyType = block.getFieldValue('KEY_TYPE');
+ const valueType = block.getFieldValue('VALUE_TYPE');
+ return [`new HashMap<${keyType}, ${valueType}>()`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['dict_get'] = function(block) {
+ const dict = Blockly.JavaScript.valueToCode(block, 'DICT', Blockly.JavaScript.ORDER_ATOMIC) || 'map';
+ const key = Blockly.JavaScript.valueToCode(block, 'KEY', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ return [`${dict}.get(${key})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['dict_set'] = function(block) {
+ const dict = Blockly.JavaScript.valueToCode(block, 'DICT', Blockly.JavaScript.ORDER_ATOMIC) || 'map';
+ const key = Blockly.JavaScript.valueToCode(block, 'KEY', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ return `${dict}.put(${key}, ${value});\n`;
+};
+
+Blockly.JavaScript['dict_remove'] = function(block) {
+ const dict = Blockly.JavaScript.valueToCode(block, 'DICT', Blockly.JavaScript.ORDER_ATOMIC) || 'map';
+ const key = Blockly.JavaScript.valueToCode(block, 'KEY', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ return `${dict}.remove(${key});\n`;
+};
+
+Blockly.JavaScript['dict_containsKey'] = function(block) {
+ const dict = Blockly.JavaScript.valueToCode(block, 'DICT', Blockly.JavaScript.ORDER_ATOMIC) || 'map';
+ const key = Blockly.JavaScript.valueToCode(block, 'KEY', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ return [`${dict}.containsKey(${key})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['dict_keys'] = function(block) {
+ const dict = Blockly.JavaScript.valueToCode(block, 'DICT', Blockly.JavaScript.ORDER_ATOMIC) || 'map';
+ return [`${dict}.keySet()`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['dict_values'] = function(block) {
+ const dict = Blockly.JavaScript.valueToCode(block, 'DICT', Blockly.JavaScript.ORDER_ATOMIC) || 'map';
+ return [`${dict}.values()`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+
+js/blocks/functions.js:
+'use strict';
+
+// Define a procedure without return
+Blockly.Blocks['procedures_defnoreturn'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('procedure')
+ .appendField(new Blockly.FieldTextInput('name'), 'NAME');
+ this.appendStatementInput('STACK')
+ .appendField('do');
+ this.setColour('#827717');
+ this.setTooltip('Defines a procedure without a return value');
+ this.setHelpUrl('');
+ }
+};
+
+// Define a function with return
+Blockly.Blocks['procedures_defreturn'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('function')
+ .appendField(new Blockly.FieldTextInput('name'), 'NAME');
+ this.appendStatementInput('STACK')
+ .appendField('do');
+ this.appendValueInput('RETURN')
+ .appendField('return');
+ this.setColour('#827717');
+ this.setTooltip('Defines a function with a return value');
+ this.setHelpUrl('');
+ }
+};
+
+// Call procedure without return
+Blockly.Blocks['procedures_callnoreturn'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('call')
+ .appendField(new Blockly.FieldTextInput('procedure'), 'NAME');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour('#827717');
+ this.setTooltip('Calls a procedure without a return value');
+ }
+};
+
+// Call function with return
+Blockly.Blocks['procedures_callreturn'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('call function')
+ .appendField(new Blockly.FieldTextInput('function'), 'NAME');
+ this.setOutput(true, null);
+ this.setColour('#827717');
+ this.setTooltip('Calls a function and retrieves the return value');
+ }
+};
+
+// Simple return block
+Blockly.Blocks['function_return'] = {
+ init: function() {
+ this.appendValueInput('VALUE')
+ .setCheck(null)
+ .appendField('return');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(false, null);
+ this.setColour('#827717');
+ this.setTooltip('Returns a value from the function');
+ }
+};
+
+// Define a function with parameters
+Blockly.Blocks['function_with_parameters'] = {
+ init: function() {
+ this.appendDummyInput("HEADER")
+ .appendField("function")
+ .appendField(new Blockly.FieldTextInput("myFunction"), "FUNCTION_NAME")
+ .appendField("return type")
+ .appendField(new Blockly.FieldDropdown([
+ ["void", "void"],
+ ["String", "String"],
+ ["int", "int"],
+ ["double", "double"],
+ ["boolean", "boolean"],
+ ["Object", "Object"]
+ ]), "RETURN_TYPE");
+ this.appendStatementInput("STACK")
+ .appendField("do");
+ this.setMutator(new Blockly.Mutator(['function_parameter']));
+ this.paramCount_ = 0;
+ this.paramNames_ = [];
+ this.setColour('#827717');
+ this.setTooltip("Defines a function with editable and 'final' parameters.");
+ },
+
+ mutationToDom: function() {
+ const container = document.createElement('mutation');
+ container.setAttribute('params', this.paramCount_);
+ this.paramNames_.forEach(param => {
+ const paramNode = document.createElement('param');
+ paramNode.setAttribute('name', param.name);
+ paramNode.setAttribute('type', param.type);
+ paramNode.setAttribute('final', param.final ? 'true' : 'false');
+ container.appendChild(paramNode);
+ });
+ return container;
+ },
+
+ domToMutation: function(xmlElement) {
+ this.paramCount_ = parseInt(xmlElement.getAttribute('params'), 10);
+ this.paramNames_ = [];
+ Array.from(xmlElement.children).forEach(param => {
+ this.paramNames_.push({
+ name: param.getAttribute('name'),
+ type: param.getAttribute('type'),
+ final: param.getAttribute('final') === 'true'
+ });
+ });
+ this.updateShape_();
+ },
+
+ decompose: function(workspace) {
+ const containerBlock = workspace.newBlock('function_mutator');
+ containerBlock.initSvg();
+ let connection = containerBlock.getInput('STACK').connection;
+
+ this.paramNames_.forEach(param => {
+ const paramBlock = workspace.newBlock('function_parameter');
+ paramBlock.setFieldValue(param.name, 'PARAM_NAME');
+ paramBlock.setFieldValue(param.type, 'PARAM_TYPE');
+ paramBlock.setFieldValue(param.final ? 'TRUE' : 'FALSE', 'FINAL');
+ paramBlock.initSvg();
+ connection.connect(paramBlock.previousConnection);
+ connection = paramBlock.nextConnection;
+ });
+
+ return containerBlock;
+ },
+
+ compose: function(containerBlock) {
+ let paramBlock = containerBlock.getInputTargetBlock('STACK');
+ const newParamNames = [];
+
+ while (paramBlock) {
+ const paramName = paramBlock.getFieldValue('PARAM_NAME');
+ const paramType = paramBlock.getFieldValue('PARAM_TYPE');
+ const isFinal = paramBlock.getFieldValue('FINAL') === 'TRUE';
+
+ newParamNames.push({
+ name: paramName,
+ type: paramType,
+ final: isFinal
+ });
+
+ paramBlock = paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
+ }
+
+ this.paramCount_ = newParamNames.length;
+ this.paramNames_ = newParamNames;
+
+ this.updateShape_();
+
+ // Update Blockly variable list
+ const workspace = this.workspace;
+ const variableNames = newParamNames.map(param => param.name);
+ workspace.getAllVariables().forEach(variable => {
+ if (!variableNames.includes(variable.name)) {
+ workspace.deleteVariableById(variable.getId());
+ }
+ });
+ variableNames.forEach(name => {
+ if (!workspace.getVariable(name)) {
+ workspace.createVariable(name);
+ }
+ });
+ },
+
+ updateShape_: function() {
+ // Remove old parameter display
+ if (this.getField("PARAMS")) {
+ this.getInput("HEADER").removeField("PARAMS");
+ }
+
+ // Add new parameter display
+ if (this.paramCount_ > 0) {
+ const paramDisplay = this.paramNames_
+ .map(param => `${param.final ? 'final ' : ''}${param.type} ${param.name}`)
+ .join(', ');
+ this.getInput("HEADER")
+ .appendField("(" + paramDisplay + ")", "PARAMS");
+ } else {
+ this.getInput("HEADER")
+ .appendField("()", "PARAMS");
+ }
+ }
+};
+
+Blockly.Blocks['function_mutator'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Parameters");
+ this.appendStatementInput("STACK")
+ .setCheck("Parameter");
+ this.setColour('#827717');
+ this.setTooltip("Add or remove parameters for the function.");
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['function_parameter'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("parameter")
+ .appendField(new Blockly.FieldTextInput("param1"), "PARAM_NAME")
+ .appendField(":")
+ .appendField(new Blockly.FieldDropdown([
+ ["String", "String"],
+ ["int", "int"],
+ ["double", "double"],
+ ["boolean", "boolean"],
+ ["Object", "Object"]
+ ]), "PARAM_TYPE")
+ .appendField("final")
+ .appendField(new Blockly.FieldCheckbox("FALSE"), "FINAL");
+ this.setPreviousStatement(true, "Parameter");
+ this.setNextStatement(true, "Parameter");
+ this.setColour('#827717');
+ this.setTooltip("Defines a parameter for the function.");
+ }
+};
+
+Blockly.JavaScript['function_with_parameters'] = function(block) {
+ const functionName = block.getFieldValue('FUNCTION_NAME');
+ const returnType = block.getFieldValue('RETURN_TYPE');
+ const paramList = block.paramNames_
+ .map(param => `${param.final ? 'final ' : ''}${param.type} ${param.name}`)
+ .join(', ');
+ const statements = Blockly.JavaScript.statementToCode(block, 'STACK');
+ return `public ${returnType} ${functionName}(${paramList}) {\n${statements}}\n`;
+};
+
+Blockly.JavaScript['function_parameter'] = function(block) {
+ const paramName = block.getFieldValue('PARAM_NAME');
+ const paramType = block.getFieldValue('PARAM_TYPE');
+ const isFinal = block.getFieldValue('FINAL') === "TRUE";
+ return `${isFinal ? 'final ' : ''}${paramType} ${paramName}`;
+};
+
+
+
+// JavaScript Generators
+Blockly.JavaScript['procedures_defnoreturn'] = function(block) {
+ const name = block.getFieldValue('NAME');
+ const statements = Blockly.JavaScript.statementToCode(block, 'STACK');
+ return `public void ${name}() {\n${statements}}\n`;
+};
+
+Blockly.JavaScript['procedures_defreturn'] = function(block) {
+ const name = block.getFieldValue('NAME');
+ const statements = Blockly.JavaScript.statementToCode(block, 'STACK');
+ const returnValue = Blockly.JavaScript.valueToCode(block, 'RETURN', Blockly.JavaScript.ORDER_ATOMIC);
+ return `public Object ${name}() {\n${statements} return ${returnValue};\n}\n`;
+};
+
+Blockly.JavaScript['procedures_callnoreturn'] = function(block) {
+ const name = block.getFieldValue('NAME');
+ return `${name}();\n`;
+};
+
+Blockly.JavaScript['procedures_callreturn'] = function(block) {
+ const name = block.getFieldValue('NAME');
+ return [`${name}()`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+
+
+js/blocks/import_blocks.js:
+// Importação Manual
+Blockly.Blocks['import_manual'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('import')
+ .appendField(new Blockly.FieldTextInput('java.util.ArrayList'), 'IMPORT_PATH');
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldCheckbox('FALSE'), 'IS_STATIC')
+ .appendField('static')
+ .appendField(new Blockly.FieldCheckbox('FALSE'), 'IS_WILDCARD')
+ .appendField('.*');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#00897B");
+ this.setTooltip('Importação manual de classe ou pacote');
+ }
+};
+
+// Importar Pacote Específico
+Blockly.Blocks['import_package'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('import package')
+ .appendField(new Blockly.FieldTextInput('com.example'), 'PACKAGE')
+ .appendField('.')
+ .appendField(new Blockly.FieldTextInput('util'), 'SUB_PACKAGE')
+ .appendField('.*');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#00897B");
+ this.setTooltip('Importar todas as classes de um pacote');
+ }
+};
+
+// Importar Classe Específica
+Blockly.Blocks['import_class'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('import class')
+ .appendField(new Blockly.FieldTextInput('java.util.List'), 'CLASS_PATH');
+ this.appendDummyInput()
+ .appendField('as')
+ .appendField(new Blockly.FieldTextInput(''), 'ALIAS')
+ .appendField('(opcional)');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#00897B");
+ this.setTooltip('Importar uma classe específica com alias opcional');
+ }
+};
+
+// Importar Método Estático
+Blockly.Blocks['import_static'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('import static')
+ .appendField(new Blockly.FieldTextInput('java.lang.Math'), 'CLASS')
+ .appendField('.')
+ .appendField(new Blockly.FieldTextInput('*'), 'METHOD');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#00897B");
+ this.setTooltip('Importar método estático de uma classe');
+ }
+};
+
+// Geradores de código
+Blockly.JavaScript['import_manual'] = function(block) {
+ let path = block.getFieldValue('IMPORT_PATH');
+ let isStatic = block.getFieldValue('IS_STATIC') === 'TRUE';
+ let isWildcard = block.getFieldValue('IS_WILDCARD') === 'TRUE';
+
+ let code = 'import ';
+ if (isStatic) code += 'static ';
+ code += path;
+ if (isWildcard) code += '.*';
+ return code + ';\n';
+};
+
+Blockly.JavaScript['import_package'] = function(block) {
+ let pkg = block.getFieldValue('PACKAGE');
+ let subPkg = block.getFieldValue('SUB_PACKAGE');
+ return `import ${pkg}.${subPkg}.*;\n`;
+};
+
+Blockly.JavaScript['import_class'] = function(block) {
+ let classPath = block.getFieldValue('CLASS_PATH');
+ let alias = block.getFieldValue('ALIAS');
+ let code = `import ${classPath}`;
+ if (alias) {
+ code += ` as ${alias}`;
+ }
+ return code + ';\n';
+};
+
+Blockly.JavaScript['import_static'] = function(block) {
+ let className = block.getFieldValue('CLASS');
+ let method = block.getFieldValue('METHOD');
+ return `import static ${className}.${method};\n`;
+};
+
+js/blocks/imported_class_blocks.js:
+// Bloco para chamar método estático
+Blockly.Blocks['imported_static_method'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput('Class'), 'CLASS_NAME')
+ .appendField('.')
+ .appendField(new Blockly.FieldTextInput('staticMethod'), 'METHOD_NAME')
+ .appendField('(');
+ this.appendStatementInput('ARGS')
+ .setCheck('method_arg');
+ this.appendDummyInput()
+ .appendField(')');
+ this.setOutput(true, null);
+ this.setColour("#6D4C41");
+ this.setTooltip('Chama um método estático de uma classe importada');
+ }
+};
+
+// Bloco para acessar campo estático
+Blockly.Blocks['imported_static_field'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput('Class'), 'CLASS_NAME')
+ .appendField('.')
+ .appendField(new Blockly.FieldTextInput('CONSTANT'), 'FIELD_NAME');
+ this.setOutput(true, null);
+ this.setColour("#6D4C41");
+ this.setTooltip('Acessa um campo estático de uma classe importada');
+ }
+};
+
+// Bloco para declarar tipo
+Blockly.Blocks['imported_type_declaration'] = {
+ init: function() {
+ this.appendValueInput('VALUE')
+ .setCheck(null)
+ .appendField(new Blockly.FieldTextInput('Type'), 'TYPE_NAME')
+ .appendField(new Blockly.FieldTextInput('varName'), 'VAR_NAME')
+ .appendField('=');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#6D4C41");
+ this.setTooltip('Declara uma variável com tipo importado');
+ }
+};
+
+// Bloco para interface
+Blockly.Blocks['imported_interface'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('implement interface')
+ .appendField(new Blockly.FieldTextInput('Interface'), 'INTERFACE_NAME');
+ this.appendStatementInput('METHODS')
+ .setCheck(null);
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#6D4C41");
+ this.setTooltip('Implementa uma interface importada');
+ }
+};
+
+// Geradores de código
+Blockly.JavaScript['imported_static_method'] = function(block) {
+ let className = block.getFieldValue('CLASS_NAME');
+ let methodName = block.getFieldValue('METHOD_NAME');
+ let args = Blockly.JavaScript.statementToCode(block, 'ARGS');
+ args = args.trim().replace(/,\s*$/, '');
+ return [`${className}.${methodName}(${args})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['imported_static_field'] = function(block) {
+ let className = block.getFieldValue('CLASS_NAME');
+ let fieldName = block.getFieldValue('FIELD_NAME');
+ return [`${className}.${fieldName}`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['imported_type_declaration'] = function(block) {
+ let typeName = block.getFieldValue('TYPE_NAME');
+ let varName = block.getFieldValue('VAR_NAME');
+ let value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC) || 'null';
+ return `${typeName} ${varName} = ${value};\n`;
+};
+
+Blockly.JavaScript['imported_interface'] = function(block) {
+ let interfaceName = block.getFieldValue('INTERFACE_NAME');
+ let methods = Blockly.JavaScript.statementToCode(block, 'METHODS');
+ return `implements ${interfaceName} {\n${methods}}\n`;
+};
+
+js/blocks/list_blocks.js:
+//blocks/list_blocks.js
+'use strict';
+
+// Create List
+Blockly.Blocks['lists_create'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('new ArrayList<')
+ .appendField(new Blockly.FieldDropdown([
+ ['String', 'String'],
+ ['Integer', 'Integer'],
+ ['Boolean', 'Boolean'],
+ ['Double', 'Double']
+ ]), 'TYPE')
+ .appendField('>');
+ this.setOutput(true, 'ArrayList');
+ this.setColour("#00ACC1");
+ this.setTooltip('Creates a new ArrayList.');
+ }
+};
+
+// List Operations
+// List Operations with target list selection
+Blockly.Blocks['lists_operation'] = {
+ init: function() {
+ this.appendValueInput('LIST')
+ .setCheck('ArrayList')
+ .appendField('Operate on list:');
+ this.appendDummyInput()
+ .appendField('Operation')
+ .appendField(new Blockly.FieldDropdown([
+ ['add', 'ADD'],
+ ['remove', 'REMOVE'],
+ ['get', 'GET'],
+ ['size', 'SIZE'],
+ ['clear', 'CLEAR']
+ ]), 'OPERATION');
+ this.appendValueInput('ITEM')
+ .setCheck(null)
+ .appendField('Item'); // Only relevant for operations like add/remove/get
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#00ACC1");
+ this.setTooltip('Perform operations on the specified ArrayList.');
+ }
+};
+
+// Get Item from List
+Blockly.Blocks['lists_get_index'] = {
+ init: function() {
+ this.appendValueInput('LIST')
+ .setCheck('ArrayList')
+ .appendField('get item from list');
+ this.appendValueInput('INDEX')
+ .setCheck('Number')
+ .appendField('at index');
+ this.setOutput(true, null);
+ this.setColour("#00ACC1");
+ this.setTooltip('Gets an item from the list at the specified index.');
+ }
+};
+
+// Set Item in List
+Blockly.Blocks['lists_set_index'] = {
+ init: function() {
+ this.appendValueInput('LIST')
+ .setCheck('ArrayList')
+ .appendField('set item in list');
+ this.appendValueInput('INDEX')
+ .setCheck('Number')
+ .appendField('at index');
+ this.appendValueInput('ITEM')
+ .appendField('to value');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#00ACC1");
+ this.setTooltip('Sets an item in the list at the specified index.');
+ }
+};
+
+// For Each Item in List
+Blockly.Blocks['lists_forEach'] = {
+ init: function() {
+ this.appendValueInput('LIST')
+ .setCheck('ArrayList')
+ .appendField('for each item in');
+ this.appendDummyInput()
+ .appendField('as')
+ .appendField(new Blockly.FieldTextInput('item'), 'VAR');
+ this.appendStatementInput('DO')
+ .appendField('do');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#00ACC1");
+ this.setTooltip('Performs actions for each item in the list.');
+ }
+};
+
+// JavaScript Generators
+Blockly.JavaScript['lists_create'] = function(block) {
+ const type = block.getFieldValue('TYPE');
+ return [`new ArrayList<${type}>()`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+// Updated JavaScript generator
+Blockly.JavaScript['lists_operation'] = function(block) {
+ const list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_ATOMIC) || 'list';
+ const item = Blockly.JavaScript.valueToCode(block, 'ITEM', Blockly.JavaScript.ORDER_ATOMIC) || 'null';
+ const operation = block.getFieldValue('OPERATION');
+
+ switch (operation) {
+ case 'ADD':
+ return `${list}.add(${item});\n`;
+ case 'REMOVE':
+ return `${list}.remove(${item});\n`;
+ case 'GET':
+ return `const result = ${list}.get(${item});\n`;
+ case 'SIZE':
+ return `const size = ${list}.size();\n`;
+ case 'CLEAR':
+ return `${list}.clear();\n`;
+ default:
+ return '';
+ }
+};
+
+Blockly.JavaScript['lists_get_index'] = function(block) {
+ const list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_ATOMIC) || 'list';
+ const index = Blockly.JavaScript.valueToCode(block, 'INDEX', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ return [`${list}.get(${index})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['lists_set_index'] = function(block) {
+ const list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_ATOMIC) || 'list';
+ const index = Blockly.JavaScript.valueToCode(block, 'INDEX', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ const item = Blockly.JavaScript.valueToCode(block, 'ITEM', Blockly.JavaScript.ORDER_ATOMIC) || 'null';
+ return `${list}.set(${index}, ${item});\n`;
+};
+
+Blockly.JavaScript['lists_forEach'] = function(block) {
+ const list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_ATOMIC) || 'list';
+ const varName = block.getFieldValue('VAR');
+ const branch = Blockly.JavaScript.statementToCode(block, 'DO') || '';
+ return `for (let ${varName} of ${list}) {\n${branch}}\n`;
+};
+
+
+js/blocks/logic_blocks.js:
+// Comparações
+Blockly.Blocks['logic_compare'] = {
+ init: function() {
+ this.appendValueInput('A')
+ .setCheck(null);
+ this.appendValueInput('B')
+ .setCheck(null)
+ .appendField(new Blockly.FieldDropdown([
+ ['=', 'EQ'],
+ ['≠', 'NEQ'],
+ ['<', 'LT'],
+ ['≤', 'LTE'],
+ ['>', 'GT'],
+ ['≥', 'GTE']
+ ]), 'OP');
+ this.setOutput(true, 'Boolean');
+ this.setColour("#FDD835");
+ this.setTooltip('Compara dois valores');
+ }
+};
+
+// Operações lógicas com múltiplas entradas
+Blockly.Blocks['logic_operation'] = {
+ init: function() {
+ this.setHelpUrl('');
+ this.setColour("#FDD835");
+ this.itemCount_ = 2;
+ this.updateShape_();
+ this.setOutput(true, 'Boolean');
+ this.setMutator(new Blockly.Mutator(['logic_operation_item']));
+ this.setTooltip('Combina valores booleanos com AND/OR');
+ },
+
+ mutationToDom: function() {
+ let container = document.createElement('mutation');
+ container.setAttribute('items', this.itemCount_);
+ return container;
+ },
+
+ domToMutation: function(xmlElement) {
+ this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
+ this.updateShape_();
+ },
+
+ decompose: function(workspace) {
+ let containerBlock = workspace.newBlock('logic_operation_container');
+ containerBlock.initSvg();
+
+ let connection = containerBlock.getInput('STACK').connection;
+ for (let i = 0; i < this.itemCount_; i++) {
+ let itemBlock = workspace.newBlock('logic_operation_item');
+ itemBlock.initSvg();
+ connection.connect(itemBlock.previousConnection);
+ connection = itemBlock.nextConnection;
+ }
+
+ return containerBlock;
+ },
+
+ compose: function(containerBlock) {
+ let itemBlock = containerBlock.getInputTargetBlock('STACK');
+ let connections = [];
+ while (itemBlock) {
+ connections.push(itemBlock.valueConnection_);
+ itemBlock = itemBlock.nextConnection &&
+ itemBlock.nextConnection.targetBlock();
+ }
+
+ this.itemCount_ = connections.length;
+ this.updateShape_();
+
+ for (let i = 0; i < this.itemCount_; i++) {
+ if (connections[i]) {
+ this.getInput('ADD' + i).connection.connect(connections[i]);
+ }
+ }
+ },
+
+ updateShape_: function() {
+ let i = 0;
+ while (this.getInput('ADD' + i)) {
+ this.removeInput('ADD' + i);
+ i++;
+ }
+
+ if (!this.getInput('OP')) {
+ this.appendDummyInput('OP')
+ .appendField(new Blockly.FieldDropdown([
+ ['and', 'AND'],
+ ['or', 'OR']
+ ]), 'OP');
+ }
+
+ for (let i = 0; i < this.itemCount_; i++) {
+ let input = this.appendValueInput('ADD' + i)
+ .setCheck('Boolean');
+ if (i === 0) {
+ input.appendField('');
+ }
+ }
+
+ if (this.itemCount_ === 0) {
+ this.appendDummyInput('EMPTY')
+ .appendField('(vazio)');
+ }
+ }
+};
+
+// Negação
+Blockly.Blocks['logic_negate'] = {
+ init: function() {
+ this.appendValueInput('BOOL')
+ .setCheck('Boolean')
+ .appendField('not');
+ this.setOutput(true, 'Boolean');
+ this.setColour("#FDD835");
+ this.setTooltip('Nega um valor booleano');
+ }
+};
+
+// Booleano
+Blockly.Blocks['logic_boolean'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldDropdown([
+ ['true', 'TRUE'],
+ ['false', 'FALSE']
+ ]), 'BOOL');
+ this.setOutput(true, 'Boolean');
+ this.setColour("#FDD835");
+ this.setTooltip('Retorna verdadeiro ou falso');
+ }
+};
+
+// Nulo
+Blockly.Blocks['logic_null'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('null');
+ this.setOutput(true, null);
+ this.setColour("#FDD835");
+ this.setTooltip('Retorna um valor nulo');
+ }
+};
+
+// Blocos do mutator para logic_operation
+Blockly.Blocks['logic_operation_container'] = {
+ init: function() {
+ this.setColour("#FDD835");
+ this.appendDummyInput()
+ .appendField('entradas');
+ this.appendStatementInput('STACK');
+ this.setTooltip("Adicione, remova ou reordene as entradas");
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['logic_operation_item'] = {
+ init: function() {
+ this.setColour("#FDD835");
+ this.appendDummyInput()
+ .appendField('valor booleano');
+ this.setPreviousStatement(true);
+ this.setNextStatement(true);
+ this.setTooltip("Adiciona uma nova entrada booleana");
+ this.contextMenu = false;
+ }
+};
+
+// Geradores de código
+Blockly.JavaScript['logic_compare'] = function(block) {
+ let a = Blockly.JavaScript.valueToCode(block, 'A', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ let b = Blockly.JavaScript.valueToCode(block, 'B', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ let op = block.getFieldValue('OP');
+
+ let code = '';
+ switch(op) {
+ case 'EQ': code = `${a}.equals(${b})`; break;
+ case 'NEQ': code = `!${a}.equals(${b})`; break;
+ case 'LT': code = `${a} < ${b}`; break;
+ case 'LTE': code = `${a} <= ${b}`; break;
+ case 'GT': code = `${a} > ${b}`; break;
+ case 'GTE': code = `${a} >= ${b}`; break;
+ }
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['logic_operation'] = function(block) {
+ let operator = block.getFieldValue('OP');
+ let code = [];
+
+ for (let i = 0; i < block.itemCount_; i++) {
+ let value = Blockly.JavaScript.valueToCode(block, 'ADD' + i,
+ Blockly.JavaScript.ORDER_ATOMIC) || 'false';
+ code.push(value);
+ }
+
+ let operation = (operator === 'AND') ? ' && ' : ' || ';
+ return ['(' + code.join(operation) + ')', Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['logic_negate'] = function(block) {
+ let bool = Blockly.JavaScript.valueToCode(block, 'BOOL', Blockly.JavaScript.ORDER_ATOMIC) || 'false';
+ return [`!${bool}`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['logic_boolean'] = function(block) {
+ let code = (block.getFieldValue('BOOL') === 'TRUE') ? 'true' : 'false';
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['logic_null'] = function(block) {
+ return ['null', Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['logic_operation_container'] = function(block) {
+ return null;
+};
+
+Blockly.JavaScript['logic_operation_item'] = function(block) {
+ return null;
+};
+
+js/blocks/math_blocks.js:
+//blocks/math_blocks.js
+'use strict';
+
+
+// Number block
+Blockly.Blocks['math_number'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldNumber(0), 'NUM');
+ this.setOutput(true, 'Number');
+ this.setColour("#8E24AA");
+ this.setTooltip('A number');
+ }
+};
+
+// Multiple Math Operations
+Blockly.Blocks['math_operation'] = {
+ init: function() {
+ this.setHelpUrl('');
+ this.setColour("#8E24AA");
+ this.itemCount_ = 2; // Começa com duas entradas
+ this.updateShape_();
+ this.setOutput(true, 'Number');
+ this.setMutator(new Blockly.Mutator(['math_operation_item']));
+ this.setTooltip('Performs mathematical operations on numbers.');
+ },
+
+ mutationToDom: function() {
+ let container = document.createElement('mutation');
+ container.setAttribute('items', this.itemCount_);
+ return container;
+ },
+
+ domToMutation: function(xmlElement) {
+ this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
+ this.updateShape_();
+ },
+
+ decompose: function(workspace) {
+ let containerBlock = workspace.newBlock('math_operation_container');
+ containerBlock.initSvg();
+
+ let connection = containerBlock.getInput('STACK').connection;
+ for (let i = 0; i < this.itemCount_ - 1; i++) {
+ let itemBlock = workspace.newBlock('math_operation_item');
+ itemBlock.initSvg();
+ connection.connect(itemBlock.previousConnection);
+ connection = itemBlock.nextConnection;
+ }
+
+ return containerBlock;
+ },
+
+ compose: function(containerBlock) {
+ let itemBlock = containerBlock.getInputTargetBlock('STACK');
+ let connections = [];
+ let operators = [];
+ while (itemBlock) {
+ connections.push(itemBlock.valueConnection_);
+ operators.push(itemBlock.getFieldValue('OP'));
+ itemBlock = itemBlock.nextConnection &&
+ itemBlock.nextConnection.targetBlock();
+ }
+
+ this.itemCount_ = connections.length + 1;
+ this.updateShape_();
+
+ for (let i = 1; i < this.itemCount_; i++) {
+ if (operators[i - 1]) {
+ this.getField('OP' + i).setValue(operators[i - 1]);
+ }
+ if (connections[i - 1]) {
+ this.getInput('NUM' + i).connection.connect(connections[i - 1]);
+ }
+ }
+ },
+
+ updateShape_: function() {
+ if (!this.getInput('NUM0')) {
+ this.appendValueInput('NUM0')
+ .setCheck('Number');
+ }
+
+ let i = 1;
+ while (this.getInput('NUM' + i)) {
+ this.removeInput('NUM' + i);
+ i++;
+ }
+
+ for (let i = 1; i < this.itemCount_; i++) {
+ this.appendValueInput('NUM' + i)
+ .setCheck('Number')
+ .appendField(new Blockly.FieldDropdown([
+ ['+', 'ADD'],
+ ['-', 'SUBTRACT'],
+ ['×', 'MULTIPLY'],
+ ['÷', 'DIVIDE']
+ ]), 'OP' + i);
+ }
+ }
+};
+
+
+// Container block for mutator
+Blockly.Blocks['math_operation_container'] = {
+ init: function() {
+ this.setColour("#8E24AA");
+ this.appendDummyInput()
+ .appendField('numbers');
+ this.appendStatementInput('STACK');
+ this.setTooltip('Add numbers for math operations');
+ this.contextMenu = false;
+ }
+};
+
+// Item block for mutator
+Blockly.Blocks['math_operation_item'] = {
+ init: function() {
+ this.setColour("#8E24AA");
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldDropdown([
+ ['+', 'ADD'],
+ ['-', 'SUBTRACT'],
+ ['×', 'MULTIPLY'],
+ ['÷', 'DIVIDE'],
+ ['%', 'MODULO'],
+ ['^', 'POWER']
+ ]), 'OP');
+ this.setPreviousStatement(true);
+ this.setNextStatement(true);
+ this.setTooltip('Adds a new number with operator');
+ this.contextMenu = false;
+ }
+};
+
+// Math functions
+Blockly.Blocks['math_function'] = {
+ init: function() {
+ this.appendValueInput('NUMBER')
+ .setCheck('Number')
+ .appendField(new Blockly.FieldDropdown([
+ ['abs', 'Math.abs'],
+ ['sqrt', 'Math.sqrt'],
+ ['pow', 'Math.pow'],
+ ['sin', 'Math.sin'],
+ ['cos', 'Math.cos'],
+ ['tan', 'Math.tan'],
+ ['round', 'Math.round'],
+ ['ceil', 'Math.ceil'],
+ ['floor', 'Math.floor'],
+ ['log', 'Math.log'],
+ ['log10', 'Math.log10'],
+ ['exp', 'Math.exp']
+ ]), 'FUNC');
+ this.setOutput(true, 'Number');
+ this.setColour("#8E24AA");
+ this.setTooltip('Advanced mathematical functions');
+ }
+};
+
+// Random with options
+Blockly.Blocks['math_random'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('random')
+ .appendField(new Blockly.FieldDropdown([
+ ['number between', 'INT'],
+ ['decimal between', 'FLOAT'],
+ ['decimal 0-1', 'RANDOM'],
+ ['boolean', 'BOOL']
+ ]), 'TYPE');
+ this.appendValueInput('MIN')
+ .setCheck('Number')
+ .appendField('min');
+ this.appendValueInput('MAX')
+ .setCheck('Number')
+ .appendField('max');
+ this.setOutput(true, ['Number', 'Boolean']);
+ this.setColour("#8E24AA");
+ this.setTooltip('Generates random numbers');
+ this.updateShape_();
+ },
+
+ updateShape_: function() {
+ let type = this.getFieldValue('TYPE');
+ if (type === 'RANDOM' || type === 'BOOL') {
+ if (this.getInput('MIN')) this.removeInput('MIN');
+ if (this.getInput('MAX')) this.removeInput('MAX');
+ } else {
+ if (!this.getInput('MIN')) {
+ this.appendValueInput('MIN')
+ .setCheck('Number')
+ .appendField('min');
+ }
+ if (!this.getInput('MAX')) {
+ this.appendValueInput('MAX')
+ .setCheck('Number')
+ .appendField('max');
+ }
+ }
+ },
+
+ onchange: function() {
+ this.updateShape_();
+ }
+};
+
+// Mathematical constants
+Blockly.Blocks['math_constant'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldDropdown([
+ ['π', 'Math.PI'],
+ ['e', 'Math.E'],
+ ['φ (phi)', '1.618034'],
+ ['√2', 'Math.SQRT2'],
+ ['√½', 'Math.SQRT1_2'],
+ ['∞', 'Infinity']
+ ]), 'CONSTANT');
+ this.setOutput(true, 'Number');
+ this.setColour("#8E24AA");
+ this.setTooltip('Mathematical constants');
+ }
+};
+
+Blockly.Blocks['math_compare'] = {
+ init: function() {
+ this.appendValueInput('A')
+ .setCheck('Number')
+ .appendField('if');
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldDropdown([
+ ['=', 'EQ'],
+ ['≠', 'NEQ'],
+ ['>', 'GT'],
+ ['≥', 'GTE'],
+ ['<', 'LT'],
+ ['≤', 'LTE']
+ ]), 'OP');
+ this.appendValueInput('B')
+ .setCheck('Number')
+ .appendField('then');
+ this.setOutput(true, 'Boolean');
+ this.setColour("#8E24AA");
+ this.setTooltip('Compares two numbers');
+ this.setHelpUrl('');
+ }
+};
+
+
+// Code Generators
+Blockly.JavaScript['math_compare'] = function(block) {
+ const valueA = Blockly.JavaScript.valueToCode(block, 'A', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ const valueB = Blockly.JavaScript.valueToCode(block, 'B', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ const operator = block.getFieldValue('OP');
+
+ let code;
+ switch (operator) {
+ case 'EQ': // Igual a
+ code = `${valueA} == ${valueB}`;
+ break;
+ case 'NEQ': // Diferente de
+ code = `${valueA} != ${valueB}`;
+ break;
+ case 'GT': // Maior que
+ code = `${valueA} > ${valueB}`;
+ break;
+ case 'GTE': // Maior ou igual a
+ code = `${valueA} >= ${valueB}`;
+ break;
+ case 'LT': // Menor que
+ code = `${valueA} < ${valueB}`;
+ break;
+ case 'LTE': // Menor ou igual a
+ code = `${valueA} <= ${valueB}`;
+ break;
+ default:
+ code = 'false';
+ }
+
+ return [code, Blockly.JavaScript.ORDER_RELATIONAL];
+};
+
+
+Blockly.JavaScript['math_number'] = function(block) {
+ let number = block.getFieldValue('NUM');
+ return [number, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['math_operation'] = function(block) {
+ let code = [];
+ let firstValue = Blockly.JavaScript.valueToCode(block, 'NUM0', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+
+ code.push(firstValue);
+
+ for (let i = 1; i < block.itemCount_; i++) {
+ let operator = block.getFieldValue('OP' + i);
+ let value = Blockly.JavaScript.valueToCode(block, 'NUM' + i, Blockly.JavaScript.ORDER_ATOMIC) || '0';
+
+ switch (operator) {
+ case 'ADD':
+ code.push('+', value);
+ break;
+ case 'SUBTRACT':
+ code.push('-', value);
+ break;
+ case 'MULTIPLY':
+ code.push('*', value);
+ break;
+ case 'DIVIDE':
+ if (value === '0') {
+ throw new Error("Division by zero error.");
+ }
+ code.push('/', value);
+ break;
+ }
+ }
+
+ return ['(' + code.join(' ') + ')', Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+
+Blockly.JavaScript['math_constant'] = function(block) {
+ let constant = block.getFieldValue('CONSTANT');
+ return [constant, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['math_function'] = function(block) {
+ let func = block.getFieldValue('FUNC');
+ let number = Blockly.JavaScript.valueToCode(block, 'NUMBER', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ return [func + '(' + number + ')', Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['math_random'] = function(block) {
+ let type = block.getFieldValue('TYPE');
+ let code;
+ let min;
+ let max
+
+ switch(type) {
+ case 'INT':
+ min = Blockly.JavaScript.valueToCode(block, 'MIN', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ max = Blockly.JavaScript.valueToCode(block, 'MAX', Blockly.JavaScript.ORDER_ATOMIC) || '100';
+ code = `Math.floor(Math.random() * (${max} - ${min} + 1) + ${min})`;
+ break;
+ case 'FLOAT':
+ min = Blockly.JavaScript.valueToCode(block, 'MIN', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ max = Blockly.JavaScript.valueToCode(block, 'MAX', Blockly.JavaScript.ORDER_ATOMIC) || '1';
+ code = `(Math.random() * (${max} - ${min}) + ${min})`;
+ break;
+ case 'RANDOM':
+ code = 'Math.random()';
+ break;
+ case 'BOOL':
+ code = 'Math.random() >= 0.5';
+ break;
+ }
+ return [code, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['math_operation_container'] = function(block) {
+ return null;
+};
+
+Blockly.JavaScript['math_operation_item'] = function(block) {
+ return null;
+};
+
+
+js/blocks/method_inserts.js:
+//blocks/method_inserts.js
+'use strict';
+Blockly.Blocks['post_runnable'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("prefix"), "PREFIX")
+ .appendField(".post new Runnable");
+ this.appendStatementInput("CONTENT")
+ .setCheck(null)
+ .appendField("content");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#4CAF50");
+ this.setTooltip("Executes code inside a Runnable using post with a prefix.");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.JavaScript['post_runnable'] = function(block) {
+ const prefix = block.getFieldValue('PREFIX');
+ const content = Blockly.JavaScript.statementToCode(block, 'CONTENT');
+ return `${prefix}.post(new Runnable() {\n @Override\n public void run() {\n${content} }\n});\n`;
+};
+
+Blockly.Blocks['run_on_ui_thread'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("prefix"), "PREFIX")
+ .appendField(".$form().runOnUiThread");
+ this.appendStatementInput("CONTENT")
+ .setCheck(null)
+ .appendField("content");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#4CAF50");
+ this.setTooltip("Runs code on the UI thread with a prefix.");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.JavaScript['run_on_ui_thread'] = function(block) {
+ const prefix = block.getFieldValue('PREFIX');
+ const content = Blockly.JavaScript.statementToCode(block, 'CONTENT');
+ return `${prefix}.runOnUiThread(new Runnable() {\n @Override\n public void run() {\n${content} }\n});\n`;
+};
+
+
+Blockly.Blocks['set_on_click_listener'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("prefix"), "PREFIX")
+ .appendField(".setOnClickListener");
+ this.appendStatementInput("CONTENT")
+ .setCheck(null)
+ .appendField("content");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#4CAF50");
+ this.setTooltip("Sets an OnClickListener with a prefix.");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.JavaScript['set_on_click_listener'] = function(block) {
+ const prefix = block.getFieldValue('PREFIX');
+ const content = Blockly.JavaScript.statementToCode(block, 'CONTENT');
+ return `${prefix}.setOnClickListener(new View.OnClickListener() {\n @Override\n public void onClick(View v) {\n${content} }\n});\n`;
+};
+
+Blockly.Blocks['add_text_view'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("prefix"), "PREFIX")
+ .appendField(".addTextView");
+ this.appendValueInput("TEXT")
+ .setCheck("String")
+ .appendField("with text");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#4CAF50");
+ this.setTooltip("Adds a TextView with specified text to a layout.");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.JavaScript['add_text_view'] = function(block) {
+ const prefix = block.getFieldValue('PREFIX');
+ const text = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_ATOMIC);
+ return `${prefix}.addView(new TextView(context) {{ setText(${text}); }});\n`;
+};
+
+Blockly.Blocks['add_image_view'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("prefix"), "PREFIX")
+ .appendField(".addImageView");
+ this.appendValueInput("IMAGE_PATH")
+ .setCheck("String")
+ .appendField("with image path");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#4CAF50");
+ this.setTooltip("Adds an ImageView with the specified image path.");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.JavaScript['add_image_view'] = function(block) {
+ const prefix = block.getFieldValue('PREFIX');
+ const imagePath = Blockly.JavaScript.valueToCode(block, 'IMAGE_PATH', Blockly.JavaScript.ORDER_ATOMIC);
+ return `${prefix}.addView(new ImageView(context) {{ setImageURI(Uri.parse(${imagePath})); }});\n`;
+};
+
+Blockly.Blocks['scroll_to_bottom'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("prefix"), "PREFIX")
+ .appendField(".scrollToBottom");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#4CAF50");
+ this.setTooltip("Scrolls a ScrollView to the bottom.");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.JavaScript['scroll_to_bottom'] = function(block) {
+ const prefix = block.getFieldValue('PREFIX');
+ return `${prefix}.post(new Runnable() {\n @Override\n public void run() {\n ${prefix}.fullScroll(View.FOCUS_DOWN);\n }\n});\n`;
+};
+
+Blockly.Blocks['show_toast'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Show Toast");
+ this.appendValueInput("MESSAGE")
+ .setCheck("String")
+ .appendField("message");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#4CAF50");
+ this.setTooltip("Shows a toast message.");
+ this.setHelpUrl("");
+ }
+};
+
+Blockly.JavaScript['show_toast'] = function(block) {
+ const message = Blockly.JavaScript.valueToCode(block, 'MESSAGE', Blockly.JavaScript.ORDER_ATOMIC);
+ return `Toast.makeText(context, ${message}, Toast.LENGTH_SHORT).show();\n`;
+};
+
+
+
+js/blocks/methods.js:
+//blocks/methods.js
+'use strict';
+
+//blocks
+// Function return block for methods
+Blockly.Blocks['method_return'] = {
+ init: function() {
+ this.appendValueInput('RETURN_VALUE')
+ .setCheck(null)
+ .appendField('return');
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(false, null); // Does not allow following block
+ this.setColour("#7CB342"); // Same color as methods
+ this.setTooltip('Returns a value from the method');
+ }
+};
+
+Blockly.Blocks['method_mutator'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Parameters");
+ this.appendStatementInput("STACK")
+ .setCheck("Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Add or remove parameters.");
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['parameter'] = {
+ init: function() {
+ this.setColour("#7CB342");
+ this.appendDummyInput()
+ .appendField('parameter');
+ this.setPreviousStatement(true);
+ this.setNextStatement(true);
+ }
+};
+
+// Updated method block with content field
+Blockly.Blocks['method_declaration'] = {
+ init: function() {
+ this.appendDummyInput("HEADER")
+ .appendField(new Blockly.FieldTextInput("myMethod"), "METHOD_NAME")
+ .appendField("return")
+ .appendField(new Blockly.FieldDropdown([
+ ["void", "void"],
+ ["String", "String"],
+ ["int", "int"],
+ ["double", "double"],
+ ["boolean", "boolean"]
+ ]), "RETURN_TYPE");
+ this.appendDummyInput("DESCRIPTION")
+ .appendField("description")
+ .appendField(new Blockly.FieldTextInput("A custom method"), "DESCRIPTION");
+ this.appendStatementInput("METHOD_CONTENT")
+ .setCheck(null)
+ .appendField("content");
+ this.setMutator(new Blockly.Mutator(['method_parameter']));
+ this.paramCount_ = 0;
+ this.paramNames_ = [];
+ this.setColour("#7CB342");
+ this.setTooltip("Declares a method with customizable and editable parameters.");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ },
+
+ mutationToDom: function() {
+ const container = document.createElement('mutation');
+ container.setAttribute('params', this.paramCount_);
+ this.paramNames_.forEach(param => {
+ const paramNode = document.createElement('param');
+ paramNode.setAttribute('name', param.name);
+ paramNode.setAttribute('type', param.type);
+ paramNode.setAttribute('final', param.final ? 'true' : 'false');
+ container.appendChild(paramNode);
+ });
+ return container;
+ },
+
+ domToMutation: function(xmlElement) {
+ this.paramCount_ = parseInt(xmlElement.getAttribute('params'), 10);
+ this.paramNames_ = [];
+ Array.from(xmlElement.children).forEach(param => {
+ this.paramNames_.push({
+ name: param.getAttribute('name'),
+ type: param.getAttribute('type'),
+ final: param.getAttribute('final') === 'true'
+ });
+ });
+ this.updateShape_();
+ },
+
+ decompose: function(workspace) {
+ const containerBlock = workspace.newBlock('method_mutator');
+ containerBlock.initSvg();
+ let connection = containerBlock.getInput('STACK').connection;
+
+ this.paramNames_.forEach(param => {
+ const paramBlock = workspace.newBlock('method_parameter');
+ paramBlock.setFieldValue(param.name, 'PARAM_NAME');
+ paramBlock.setFieldValue(param.type, 'PARAM_TYPE');
+ paramBlock.setFieldValue(param.final ? 'TRUE' : 'FALSE', 'FINAL');
+ paramBlock.initSvg();
+ connection.connect(paramBlock.previousConnection);
+ connection = paramBlock.nextConnection;
+ });
+
+ return containerBlock;
+ },
+
+ compose: function(containerBlock) {
+ let paramBlock = containerBlock.getInputTargetBlock('STACK');
+ const newParamNames = [];
+
+ while (paramBlock) {
+ const paramName = paramBlock.getFieldValue('PARAM_NAME') || `param${this.paramCount_ + 1}`;
+ const paramType = paramBlock.getFieldValue('PARAM_TYPE') || "String";
+ const isFinal = paramBlock.getFieldValue('FINAL') === "TRUE";
+
+ newParamNames.push({
+ name: paramName,
+ type: paramType,
+ final: isFinal
+ });
+
+ paramBlock = paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
+ }
+
+ this.paramCount_ = newParamNames.length;
+ this.paramNames_ = newParamNames;
+
+ this.updateShape_();
+
+ // Update Blockly variable list
+ const workspace = this.workspace;
+ const variableNames = newParamNames.map(param => param.name);
+ workspace.getAllVariables().forEach(variable => {
+ if (!variableNames.includes(variable.name)) {
+ workspace.deleteVariableById(variable.getId());
+ }
+ });
+ variableNames.forEach(name => {
+ if (!workspace.getVariable(name)) {
+ workspace.createVariable(name);
+ }
+ });
+ },
+
+ updateShape_: function() {
+ // Remove old parameter display
+ if (this.getField("PARAMS")) {
+ this.getInput("HEADER").removeField("PARAMS");
+ }
+
+ // Add new parameter display
+ if (this.paramCount_ > 0) {
+ const paramDisplay = this.paramNames_
+ .map(param => `${param.final ? 'final ' : ''}${param.type} ${param.name}`)
+ .join(', ');
+ this.getInput("HEADER")
+ .appendField("(" + paramDisplay + ")", "PARAMS");
+ } else {
+ this.getInput("HEADER")
+ .appendField("()", "PARAMS");
+ }
+ }
+};
+
+Blockly.Blocks['method_mutator'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Parameters");
+ this.appendStatementInput("STACK")
+ .setCheck("Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Add or remove parameters for the method.");
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['method_parameter'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("parameter")
+ .appendField(new Blockly.FieldTextInput("param1"), "PARAM_NAME")
+ .appendField(":")
+ .appendField(new Blockly.FieldDropdown([
+ ["String", "String"],
+ ["int", "int"],
+ ["double", "double"],
+ ["boolean", "boolean"],
+ ["Object", "Object"]
+ ]), "PARAM_TYPE")
+ .appendField("final")
+ .appendField(new Blockly.FieldCheckbox("FALSE"), "FINAL");
+ this.setPreviousStatement(true, "Parameter");
+ this.setNextStatement(true, "Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Defines a parameter for the method.");
+ }
+};
+
+Blockly.JavaScript['method_declaration'] = function(block) {
+ const methodName = block.getFieldValue('METHOD_NAME');
+ const description = block.getFieldValue('DESCRIPTION');
+ const returnType = block.getFieldValue('RETURN_TYPE');
+ const content = Blockly.JavaScript.statementToCode(block, 'METHOD_CONTENT');
+
+ const parameters = block.paramNames_ || [];
+ const paramString = parameters.map(param => `${param.final ? 'final ' : ''}${param.type} ${param.name}`).join(', ');
+
+ return ` @SimpleFunction(description = "${description}")
+ public ${returnType} ${methodName}(${paramString}) {
+${content}
+ }\n`;
+};
+
+Blockly.JavaScript['method_parameter'] = function(block) {
+ const paramName = block.getFieldValue('PARAM_NAME');
+ const paramType = block.getFieldValue('PARAM_TYPE');
+ const isFinal = block.getFieldValue('FINAL') === "TRUE";
+ return `${isFinal ? 'final ' : ''}${paramType} ${paramName}`;
+};
+
+
+//Events
+Blockly.Blocks['event_declaration'] = {
+ init: function() {
+ this.appendDummyInput("HEADER")
+ .appendField(new Blockly.FieldTextInput("MyEvent"), "EVENT_NAME")
+ .appendField("parameters");
+ this.appendDummyInput("DESCRIPTION")
+ .appendField("description")
+ .appendField(new Blockly.FieldTextInput("A custom event"), "DESCRIPTION");
+ this.appendStatementInput("EVENT_CONTENT")
+ .setCheck(null)
+ .appendField("content");
+ this.setMutator(new Blockly.Mutator(['event_parameter']));
+ this.paramCount_ = 0;
+ this.paramNames_ = [];
+ this.setColour("#7CB342");
+ this.setTooltip("Declares an event with customizable parameters.");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ },
+
+ mutationToDom: function() {
+ const container = document.createElement('mutation');
+ container.setAttribute('params', this.paramCount_);
+ this.paramNames_.forEach(param => {
+ const paramNode = document.createElement('param');
+ paramNode.setAttribute('name', param.name);
+ paramNode.setAttribute('type', param.type);
+ paramNode.setAttribute('final', param.final ? 'true' : 'false');
+ container.appendChild(paramNode);
+ });
+ return container;
+ },
+
+ domToMutation: function(xmlElement) {
+ this.paramCount_ = parseInt(xmlElement.getAttribute('params'), 10);
+ this.paramNames_ = [];
+ Array.from(xmlElement.children).forEach(param => {
+ this.paramNames_.push({
+ name: param.getAttribute('name'),
+ type: param.getAttribute('type'),
+ final: param.getAttribute('final') === 'true'
+ });
+ });
+ this.updateShape_();
+ },
+
+ decompose: function(workspace) {
+ const containerBlock = workspace.newBlock('event_mutator');
+ containerBlock.initSvg();
+ let connection = containerBlock.getInput('STACK').connection;
+
+ this.paramNames_.forEach(param => {
+ const paramBlock = workspace.newBlock('event_parameter');
+ paramBlock.setFieldValue(param.name, 'PARAM_NAME');
+ paramBlock.setFieldValue(param.type, 'PARAM_TYPE');
+ paramBlock.setFieldValue(param.final ? 'TRUE' : 'FALSE', 'FINAL');
+ paramBlock.initSvg();
+ connection.connect(paramBlock.previousConnection);
+ connection = paramBlock.nextConnection;
+ });
+
+ return containerBlock;
+ },
+
+ compose: function(containerBlock) {
+ let paramBlock = containerBlock.getInputTargetBlock('STACK');
+ const newParamNames = [];
+
+ while (paramBlock) {
+ const paramName = paramBlock.getFieldValue('PARAM_NAME') || `param${this.paramCount_ + 1}`;
+ const paramType = paramBlock.getFieldValue('PARAM_TYPE') || "String";
+ const isFinal = paramBlock.getFieldValue('FINAL') === "TRUE";
+
+ newParamNames.push({
+ name: paramName,
+ type: paramType,
+ final: isFinal
+ });
+
+ paramBlock = paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
+ }
+
+ this.paramCount_ = newParamNames.length;
+ this.paramNames_ = newParamNames;
+
+ this.updateShape_();
+
+ // Update Blockly variable list
+ const workspace = this.workspace;
+ const variableNames = newParamNames.map(param => param.name);
+ workspace.getAllVariables().forEach(variable => {
+ if (!variableNames.includes(variable.name)) {
+ workspace.deleteVariableById(variable.getId());
+ }
+ });
+ variableNames.forEach(name => {
+ if (!workspace.getVariable(name)) {
+ workspace.createVariable(name);
+ }
+ });
+ },
+
+ updateShape_: function() {
+ if (this.getField("PARAMS")) {
+ this.getInput("HEADER").removeField("PARAMS");
+ }
+
+ if (this.paramCount_ > 0) {
+ const paramDisplay = this.paramNames_
+ .map(param => `${param.final ? 'final ' : ''}${param.type} ${param.name}`)
+ .join(', ');
+ this.getInput("HEADER")
+ .appendField("(" + paramDisplay + ")", "PARAMS");
+ } else {
+ this.getInput("HEADER")
+ .appendField("()", "PARAMS");
+ }
+ }
+};
+
+Blockly.Blocks['event_mutator'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Parameters");
+ this.appendStatementInput("STACK")
+ .setCheck("Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Add or remove parameters for the event.");
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['event_parameter'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("parameter")
+ .appendField(new Blockly.FieldTextInput("param1"), "PARAM_NAME")
+ .appendField(":")
+ .appendField(new Blockly.FieldDropdown([
+ ["String", "String"],
+ ["int", "int"],
+ ["double", "double"],
+ ["boolean", "boolean"]
+ ]), "PARAM_TYPE")
+ .appendField("final")
+ .appendField(new Blockly.FieldCheckbox("FALSE"), "FINAL");
+ this.setPreviousStatement(true, "Parameter");
+ this.setNextStatement(true, "Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Defines a parameter for the event.");
+ }
+};
+
+Blockly.JavaScript['event_declaration'] = function(block) {
+ const eventName = block.getFieldValue('EVENT_NAME');
+ const description = block.getFieldValue('DESCRIPTION');
+ const content = Blockly.JavaScript.statementToCode(block, 'EVENT_CONTENT');
+
+ const parameters = block.paramNames_ || [];
+ const paramString = parameters.map(param => `${param.final ? 'final ' : ''}${param.type} ${param.name}`).join(', ');
+
+ return ` @SimpleEvent(description = "${description}")
+ public void ${eventName}(${paramString}) {
+${content}
+ }\n`;
+};
+
+Blockly.JavaScript['event_parameter'] = function(block) {
+ const paramName = block.getFieldValue('PARAM_NAME');
+ const paramType = block.getFieldValue('PARAM_TYPE');
+ const isFinal = block.getFieldValue('FINAL') === "TRUE";
+ return `${isFinal ? 'final ' : ''}${paramType} ${paramName}`;
+};
+
+
+//Custom_dispatcher
+Blockly.Blocks['custom_dispatcher'] = {
+ init: function() {
+ this.appendDummyInput("HEADER")
+ .appendField(new Blockly.FieldTextInput("Dispatcher"), "EVENT_NAME")
+ .appendField("parameters");
+ this.setMutator(new Blockly.Mutator(['custom_dispatcher_parameter']));
+ this.paramCount_ = 0;
+ this.paramNames_ = [];
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#7CB342");
+ this.setTooltip("Custom Event Dispatcher with dynamic parameters.");
+ },
+
+ mutationToDom: function() {
+ const container = document.createElement('mutation');
+ container.setAttribute('params', this.paramCount_);
+ this.paramNames_.forEach(param => {
+ const paramNode = document.createElement('param');
+ paramNode.setAttribute('name', param.name);
+ paramNode.setAttribute('type', param.type);
+ container.appendChild(paramNode);
+ });
+ return container;
+ },
+
+ domToMutation: function(xmlElement) {
+ this.paramCount_ = parseInt(xmlElement.getAttribute('params'), 10);
+ this.paramNames_ = [];
+ Array.from(xmlElement.children).forEach(param => {
+ this.paramNames_.push({
+ name: param.getAttribute('name'),
+ type: param.getAttribute('type')
+ });
+ });
+ this.updateShape_();
+ },
+
+ decompose: function(workspace) {
+ const containerBlock = workspace.newBlock('custom_dispatcher_mutator');
+ containerBlock.initSvg();
+ let connection = containerBlock.getInput('STACK').connection;
+
+ this.paramNames_.forEach(param => {
+ const paramBlock = workspace.newBlock('custom_dispatcher_parameter');
+ paramBlock.setFieldValue(param.name, 'PARAM_NAME');
+ paramBlock.setFieldValue(param.type, 'PARAM_TYPE');
+ paramBlock.initSvg();
+ connection.connect(paramBlock.previousConnection);
+ connection = paramBlock.nextConnection;
+ });
+
+ return containerBlock;
+ },
+
+ compose: function(containerBlock) {
+ let paramBlock = containerBlock.getInputTargetBlock('STACK');
+ const newParamNames = [];
+
+ while (paramBlock) {
+ const paramName = paramBlock.getFieldValue('PARAM_NAME') || `param${this.paramCount_ + 1}`;
+ const paramType = paramBlock.getFieldValue('PARAM_TYPE') || "String";
+
+ newParamNames.push({
+ name: paramName,
+ type: paramType
+ });
+
+ paramBlock = paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
+ }
+
+ this.paramCount_ = newParamNames.length;
+ this.paramNames_ = newParamNames;
+
+ this.updateShape_();
+ },
+
+ updateShape_: function() {
+ if (this.getField("PARAMS")) {
+ this.getInput("HEADER").removeField("PARAMS");
+ }
+
+ if (this.paramCount_ > 0) {
+ const paramDisplay = this.paramNames_
+ .map(param => `${param.type} ${param.name}`)
+ .join(', ');
+ this.getInput("HEADER")
+ .appendField("(" + paramDisplay + ")", "PARAMS");
+ } else {
+ this.getInput("HEADER")
+ .appendField("()", "PARAMS");
+ }
+ }
+};
+
+Blockly.Blocks['custom_dispatcher_mutator'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Parameters");
+ this.appendStatementInput("STACK")
+ .setCheck("Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Add or remove parameters for the dispatcher.");
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['custom_dispatcher_parameter'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("parameter")
+ .appendField(new Blockly.FieldTextInput("param1"), "PARAM_NAME")
+ .appendField(":")
+ .appendField(new Blockly.FieldDropdown([
+ ["String", "String"],
+ ["int", "int"],
+ ["double", "double"],
+ ["boolean", "boolean"]
+ ]), "PARAM_TYPE");
+ this.setPreviousStatement(true, "Parameter");
+ this.setNextStatement(true, "Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Defines a parameter for the dispatcher.");
+ }
+};
+
+Blockly.JavaScript['custom_dispatcher'] = function(block) {
+ const eventName = block.getFieldValue('EVENT_NAME');
+
+ const parameters = block.paramNames_ || [];
+ const paramString = parameters.map(param => `${param.type} ${param.name}`).join(', ');
+ const paramNames = parameters.map(param => param.name).join(', ');
+
+ return ` EventDispatcher.dispatchEvent(this, "${eventName}", ${paramNames});\n`;
+};
+
+Blockly.Blocks['method_call'] = {
+ init: function() {
+ this.appendDummyInput("HEADER")
+ .appendField("call method")
+ .appendField(new Blockly.FieldTextInput("Method Name"), "METHOD_NAME");
+ this.setMutator(new Blockly.Mutator(['custom_dispatcher_parameter']));
+ this.paramCount_ = 0;
+ this.paramNames_ = [];
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#7CB342");
+ this.setTooltip("Call a method with dynamic parameters.");
+ },
+
+ mutationToDom: function() {
+ const container = document.createElement('mutation');
+ container.setAttribute('params', this.paramCount_);
+ this.paramNames_.forEach(param => {
+ const paramNode = document.createElement('param');
+ paramNode.setAttribute('name', param.name);
+ paramNode.setAttribute('type', param.type);
+ container.appendChild(paramNode);
+ });
+ return container;
+ },
+
+ domToMutation: function(xmlElement) {
+ this.paramCount_ = parseInt(xmlElement.getAttribute('params'), 10);
+ this.paramNames_ = [];
+ Array.from(xmlElement.children).forEach(param => {
+ this.paramNames_.push({
+ name: param.getAttribute('name'),
+ type: param.getAttribute('type')
+ });
+ });
+ this.updateShape_();
+ },
+
+ decompose: function(workspace) {
+ const containerBlock = workspace.newBlock('call_event_mutator');
+ containerBlock.initSvg();
+ const connection = containerBlock.getInput('STACK').connection;
+
+ this.paramNames_.forEach(param => {
+ const paramBlock = workspace.newBlock('call_event_parameter');
+ paramBlock.setFieldValue(param.name, 'PARAM_NAME');
+ paramBlock.setFieldValue(param.type, 'PARAM_TYPE');
+ paramBlock.initSvg();
+ connection.connect(paramBlock.previousConnection);
+ connection = paramBlock.nextConnection;
+ });
+
+ return containerBlock;
+ },
+
+ compose: function(containerBlock) {
+ let paramBlock = containerBlock.getInputTargetBlock('STACK');
+ this.paramCount_ = 0;
+ this.paramNames_ = [];
+
+ while (paramBlock) {
+ const paramName = paramBlock.getFieldValue('PARAM_NAME');
+ const paramType = paramBlock.getFieldValue('PARAM_TYPE');
+
+ this.paramCount_++;
+ this.paramNames_.push({
+ name: paramName,
+ type: paramType
+ });
+ paramBlock = paramBlock.nextConnection && paramBlock.nextConnection.targetBlock();
+ }
+
+ this.updateShape_();
+ },
+
+ updateShape_: function() {
+ // Remove the old parameter display
+ if (this.getField("PARAMS")) {
+ this.getInput("HEADER").removeField("PARAMS");
+ }
+
+ // Add the new parameter display
+ if (this.paramCount_ > 0) {
+ const paramDisplay = this.paramNames_
+ .map(param => `${param.type} ${param.name}`)
+ .join(', ');
+ this.getInput("HEADER")
+ .appendField("(" + paramDisplay + ")", "PARAMS");
+ } else {
+ this.getInput("HEADER")
+ .appendField("()", "PARAMS");
+ }
+ }
+};
+
+Blockly.Blocks['call_event_mutator'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField("Parameters");
+ this.appendStatementInput("STACK")
+ .setCheck("Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Add or remove parameters for the custom dispatcher.");
+ this.contextMenu = false;
+ }
+};
+
+Blockly.Blocks['call_event_parameter'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldTextInput("param1"), "PARAM_NAME")
+ .appendField(":")
+ .appendField(new Blockly.FieldDropdown([
+ ["String", "String"],
+ ["int", "int"],
+ ["double", "double"],
+ ["boolean", "boolean"]
+ ]), "PARAM_TYPE");
+ this.setPreviousStatement(true, "Parameter");
+ this.setNextStatement(true, "Parameter");
+ this.setColour("#7CB342");
+ this.setTooltip("Defines a parameter for the custom dispatcher.");
+ }
+};
+
+
+
+//generators
+
+Blockly.JavaScript['method_call'] = function(block) {
+ const eventName = block.getFieldValue('METHOD_NAME');
+ const content = Blockly.JavaScript.statementToCode(block, 'EVENT_CONTENT');
+
+ // Coleta os parâmetros diretamente do `paramNames_`
+ const parameters = block.paramNames_ || [];
+
+ // Junta os parâmetros no formato "tipo nome" para o cabeçalho
+ const paramString = parameters.map(param => `${param.type} ${param.name}`).join(', ');
+
+ // Apenas os nomes dos parâmetros para usar no `EventDispatcher.dispatchEvent`
+ const paramNames = parameters.map(param => param.name).join(', ');
+
+ // Gera o evento com os parâmetros no cabeçalho e no dispatcher
+ return `${eventName}(${paramNames});\n`;
+};
+
+
+Blockly.JavaScript['method_parameter'] = function(block) {
+ const paramName = block.getFieldValue('PARAM_NAME');
+ const paramType = block.getFieldValue('PARAM_TYPE');
+ const paramFinal = block.getFieldValue('FINAL') === 'TRUE';
+
+ // Retorna o parâmetro formatado como "tipo nome"
+ return (paramFinal ? 'final ' : '') + `${paramType} ${paramName}`;
+};
+
+
+Blockly.JavaScript['method_return'] = function(block) {
+ let returnValue = Blockly.JavaScript.valueToCode(block, 'RETURN_VALUE', Blockly.JavaScript.ORDER_ATOMIC) || '';
+ return ` return ${returnValue};\n`;
+};
+
+js/blocks/object_blocks.js:
+// Criar Objeto
+Blockly.Blocks['object_create'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('new')
+ .appendField(new Blockly.FieldTextInput('Object'), 'CLASS_NAME');
+ this.appendStatementInput('CONSTRUCTOR_ARGS')
+ .setCheck('constructor_arg')
+ .appendField('argumentos');
+ this.setOutput(true, 'Object');
+ this.setColour("#F4511E");;
+ this.setTooltip('Cria uma nova instância de objeto');
+ }
+};
+
+// Argumento do Construtor
+Blockly.Blocks['constructor_arg'] = {
+ init: function() {
+ this.appendValueInput('ARG_VALUE')
+ .setCheck(null)
+ .appendField(new Blockly.FieldTextInput('arg'), 'ARG_NAME');
+ this.setPreviousStatement(true, 'constructor_arg');
+ this.setNextStatement(true, 'constructor_arg');
+ this.setColour("#F4511E");;
+ this.setTooltip('Argumento para construtor');
+ }
+};
+
+// Acessar Campo
+Blockly.Blocks['object_get_field'] = {
+ init: function() {
+ this.appendValueInput('OBJECT')
+ .setCheck('Object');
+ this.appendDummyInput()
+ .appendField('.')
+ .appendField(new Blockly.FieldTextInput('field'), 'FIELD_NAME');
+ this.setOutput(true, null);
+ this.setColour("#F4511E");;
+ this.setTooltip('Acessa um campo do objeto');
+ }
+};
+
+// Definir Campo
+Blockly.Blocks['object_set_field'] = {
+ init: function() {
+ this.appendValueInput('OBJECT')
+ .setCheck('Object');
+ this.appendDummyInput()
+ .appendField('.')
+ .appendField(new Blockly.FieldTextInput('field'), 'FIELD_NAME')
+ .appendField('=');
+ this.appendValueInput('VALUE')
+ .setCheck(null);
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#F4511E");;
+ this.setTooltip('Define o valor de um campo do objeto');
+ }
+};
+
+// Chamar Método
+Blockly.Blocks['object_call_method'] = {
+ init: function() {
+ this.appendValueInput('OBJECT')
+ .setCheck('Object');
+ this.appendDummyInput()
+ .appendField('.')
+ .appendField(new Blockly.FieldTextInput('method'), 'METHOD_NAME')
+ .appendField('(');
+ this.appendStatementInput('ARGS')
+ .setCheck('method_arg');
+ this.appendDummyInput()
+ .appendField(')');
+ this.setOutput(true, null);
+ this.setColour("#F4511E");;
+ this.setTooltip('Chama um método do objeto');
+ }
+};
+
+// Argumento de Método
+Blockly.Blocks['method_arg'] = {
+ init: function() {
+ this.appendValueInput('ARG_VALUE')
+ .setCheck(null);
+ this.setPreviousStatement(true, 'method_arg');
+ this.setNextStatement(true, 'method_arg');
+ this.setColour("#F4511E");;
+ this.setTooltip('Argumento para método');
+ }
+};
+
+// Instanceof
+Blockly.Blocks['object_instance_of'] = {
+ init: function() {
+ this.appendValueInput('OBJECT')
+ .setCheck('Object');
+ this.appendDummyInput()
+ .appendField('instanceof')
+ .appendField(new Blockly.FieldTextInput('Type'), 'TYPE');
+ this.setOutput(true, 'Boolean');
+ this.setColour("#F4511E");;
+ this.setTooltip('Verifica se o objeto é instância de um tipo');
+ }
+};
+
+// Cast
+Blockly.Blocks['object_cast'] = {
+ init: function() {
+ this.appendValueInput('OBJECT')
+ .setCheck('Object')
+ .appendField('(')
+ .appendField(new Blockly.FieldTextInput('Type'), 'TYPE')
+ .appendField(')');
+ this.setOutput(true, 'Object');
+ this.setColour("#F4511E");;
+ this.setTooltip('Converte o objeto para outro tipo');
+ }
+};
+
+// Geradores de código
+Blockly.JavaScript['object_create'] = function(block) {
+ let className = block.getFieldValue('CLASS_NAME');
+ let args = Blockly.JavaScript.statementToCode(block, 'CONSTRUCTOR_ARGS');
+ args = args.trim().replace(/,\s*$/, ''); // Remove última vírgula
+ return [`new ${className}(${args})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['constructor_arg'] = function(block) {
+ let value = Blockly.JavaScript.valueToCode(block, 'ARG_VALUE', Blockly.JavaScript.ORDER_ATOMIC) || 'null';
+ return value + ', ';
+};
+
+Blockly.JavaScript['object_get_field'] = function(block) {
+ let object = Blockly.JavaScript.valueToCode(block, 'OBJECT', Blockly.JavaScript.ORDER_ATOMIC) || 'this';
+ let field = block.getFieldValue('FIELD_NAME');
+ return [`${object}.${field}`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['object_set_field'] = function(block) {
+ let object = Blockly.JavaScript.valueToCode(block, 'OBJECT', Blockly.JavaScript.ORDER_ATOMIC) || 'this';
+ let field = block.getFieldValue('FIELD_NAME');
+ let value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC) || 'null';
+ return `${object}.${field} = ${value};\n`;
+};
+
+Blockly.JavaScript['object_call_method'] = function(block) {
+ let object = Blockly.JavaScript.valueToCode(block, 'OBJECT', Blockly.JavaScript.ORDER_ATOMIC) || 'this';
+ let method = block.getFieldValue('METHOD_NAME');
+ let args = Blockly.JavaScript.statementToCode(block, 'ARGS');
+ args = args.trim().replace(/,\s*$/, ''); // Remove última vírgula
+ return [`${object}.${method}(${args})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['method_arg'] = function(block) {
+ let value = Blockly.JavaScript.valueToCode(block, 'ARG_VALUE', Blockly.JavaScript.ORDER_ATOMIC) || 'null';
+ return value + ', ';
+};
+
+Blockly.JavaScript['object_instance_of'] = function(block) {
+ let object = Blockly.JavaScript.valueToCode(block, 'OBJECT', Blockly.JavaScript.ORDER_ATOMIC) || 'this';
+ let type = block.getFieldValue('TYPE');
+ return [`${object} instanceof ${type}`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['object_cast'] = function(block) {
+ let object = Blockly.JavaScript.valueToCode(block, 'OBJECT', Blockly.JavaScript.ORDER_ATOMIC) || 'null';
+ let type = block.getFieldValue('TYPE');
+ return [`(${type}) ${object}`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+js/blocks/text_blocks.js:
+//blocks/text_blocks.js
+'use strict';
+
+// Simple Text Block
+Blockly.Blocks['text_string'] = {
+ init: function() {
+ this.appendDummyInput()
+ .appendField('"')
+ .appendField(new Blockly.FieldTextInput(''), 'TEXT')
+ .appendField('"');
+ this.setOutput(true, 'String');
+ this.setColour("#3949AB");
+ this.setTooltip('Text string');
+ }
+};
+
+Blockly.Blocks['text_compare'] = {
+ init: function() {
+ this.appendValueInput('TEXT1')
+ .setCheck('String')
+ .appendField('compare text');
+ this.appendDummyInput()
+ .appendField(new Blockly.FieldDropdown([
+ ['is equal to', 'EQ'],
+ ['is not equal to', 'NEQ'],
+ ['contains', 'CONTAINS'],
+ ['does not contain', 'NOT_CONTAINS']
+ ]), 'OP');
+ this.appendValueInput('TEXT2')
+ .setCheck('String')
+ .appendField('with text');
+ this.setOutput(true, 'Boolean');
+ this.setColour("#3949AB");
+ this.setTooltip('Compares two texts or checks if one contains the other.');
+ this.setHelpUrl('');
+ }
+};
+
+// Concatenate Texts Block
+Blockly.Blocks['text_join'] = {
+ init: function() {
+ this.appendValueInput('A')
+ .setCheck(['String', 'Number']);
+ this.appendValueInput('B')
+ .setCheck(['String', 'Number'])
+ .appendField('+');
+ this.setOutput(true, 'String');
+ this.setColour("#3949AB");
+ this.setTooltip('Concatenates texts');
+ }
+};
+
+// Text Operations Block
+Blockly.Blocks['text_operation'] = {
+ init: function() {
+ this.appendValueInput('TEXT')
+ .setCheck('String')
+ .appendField(new Blockly.FieldDropdown([
+ ['length', 'LENGTH'],
+ ['to uppercase', 'UPPER'],
+ ['to lowercase', 'LOWER'],
+ ['trim', 'TRIM']
+ ]), 'OP');
+ this.setOutput(true, ['String', 'Number']);
+ this.setColour("#3949AB");
+ this.setTooltip('Text operations');
+ }
+};
+
+// Character at index Block
+Blockly.Blocks['text_charAt'] = {
+ init: function() {
+ this.appendValueInput('STRING')
+ .setCheck('String')
+ .appendField('character at');
+ this.appendValueInput('INDEX')
+ .setCheck('Number')
+ .appendField('position');
+ this.setOutput(true, 'String');
+ this.setColour("#3949AB");
+ this.setTooltip('Returns the character at the specified position in the string');
+ }
+};
+
+// Find Index of Substring Block
+Blockly.Blocks['text_indexOf'] = {
+ init: function() {
+ this.appendValueInput('STRING')
+ .setCheck('String')
+ .appendField('find in');
+ this.appendValueInput('SUBSTRING')
+ .setCheck('String')
+ .appendField('substring');
+ this.setOutput(true, 'Number');
+ this.setColour("#3949AB");
+ this.setTooltip('Finds the position of a substring in the text');
+ }
+};
+
+// Replace Text Block
+Blockly.Blocks['text_replace'] = {
+ init: function() {
+ this.appendValueInput('STRING')
+ .setCheck('String')
+ .appendField('replace in');
+ this.appendValueInput('TARGET')
+ .setCheck('String')
+ .appendField('target');
+ this.appendValueInput('REPLACEMENT')
+ .setCheck('String')
+ .appendField('with');
+ this.setOutput(true, 'String');
+ this.setColour("#3949AB");
+ this.setTooltip('Replaces occurrences of the target text with replacement text');
+ }
+};
+
+// Split Text Block
+Blockly.Blocks['text_split'] = {
+ init: function() {
+ this.appendValueInput('STRING')
+ .setCheck('String')
+ .appendField('split');
+ this.appendValueInput('DELIMITER')
+ .setCheck('String')
+ .appendField('by');
+ this.setOutput(true, 'Array');
+ this.setColour("#3949AB");
+ this.setTooltip('Splits the string into an array using a delimiter');
+ }
+};
+
+// JavaScript Generators
+Blockly.JavaScript['text_compare'] = function(block) {
+ const text1 = Blockly.JavaScript.valueToCode(block, 'TEXT1', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const text2 = Blockly.JavaScript.valueToCode(block, 'TEXT2', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const operator = block.getFieldValue('OP');
+
+ let code;
+ switch (operator) {
+ case 'EQ': // Igual a
+ code = `${text1} === ${text2}`;
+ break;
+ case 'NEQ': // Diferente de
+ code = `${text1} !== ${text2}`;
+ break;
+ case 'CONTAINS': // Contém
+ code = `${text1}.includes(${text2})`;
+ break;
+ case 'NOT_CONTAINS': // Não contém
+ code = `!${text1}.includes(${text2})`;
+ break;
+ default:
+ code = 'false';
+ }
+
+ return [code, Blockly.JavaScript.ORDER_RELATIONAL];
+};
+
+Blockly.JavaScript['text_string'] = function(block) {
+ const text = block.getFieldValue('TEXT');
+ return [`"${text}"`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['text_join'] = function(block) {
+ const a = Blockly.JavaScript.valueToCode(block, 'A', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const b = Blockly.JavaScript.valueToCode(block, 'B', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ return [`${a} + ${b}`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['text_operation'] = function(block) {
+ const text = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const op = block.getFieldValue('OP');
+ switch (op) {
+ case 'LENGTH':
+ return [`${text}.length()`, Blockly.JavaScript.ORDER_ATOMIC];
+ case 'UPPER':
+ return [`${text}.toUpperCase()`, Blockly.JavaScript.ORDER_ATOMIC];
+ case 'LOWER':
+ return [`${text}.toLowerCase()`, Blockly.JavaScript.ORDER_ATOMIC];
+ case 'TRIM':
+ return [`${text}.trim()`, Blockly.JavaScript.ORDER_ATOMIC];
+ default:
+ return ['""', Blockly.JavaScript.ORDER_ATOMIC];
+ }
+};
+
+Blockly.JavaScript['text_charAt'] = function(block) {
+ const string = Blockly.JavaScript.valueToCode(block, 'STRING', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const index = Blockly.JavaScript.valueToCode(block, 'INDEX', Blockly.JavaScript.ORDER_ATOMIC) || '0';
+ return [`${string}.charAt(${index})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['text_indexOf'] = function(block) {
+ const string = Blockly.JavaScript.valueToCode(block, 'STRING', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const substring = Blockly.JavaScript.valueToCode(block, 'SUBSTRING', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ return [`${string}.indexOf(${substring})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['text_replace'] = function(block) {
+ const string = Blockly.JavaScript.valueToCode(block, 'STRING', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const target = Blockly.JavaScript.valueToCode(block, 'TARGET', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const replacement = Blockly.JavaScript.valueToCode(block, 'REPLACEMENT', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ return [`${string}.replace(${target}, ${replacement})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+Blockly.JavaScript['text_split'] = function(block) {
+ const string = Blockly.JavaScript.valueToCode(block, 'STRING', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ const delimiter = Blockly.JavaScript.valueToCode(block, 'DELIMITER', Blockly.JavaScript.ORDER_ATOMIC) || '""';
+ return [`${string}.split(${delimiter})`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+
+js/blocks/variables.js:
+//blocks/variables.js
+'use strict';
+
+Blockly.Blocks['create_variable'] = {
+ init: function () {
+ this.appendDummyInput()
+ .appendField("Create Variable")
+ .appendField(new Blockly.FieldTextInput("varName", this.validateAndRenameVariable.bind(this)), "VAR_NAME")
+ .appendField("type")
+ .appendField(new Blockly.FieldDropdown([
+ ["int", "int"],
+ ["String", "String"],
+ ["boolean", "boolean"],
+ ["double", "double"],
+ ["Object", "Object"],
+ ["List", "List"],
+ ["List", "List"]
+ ]), "VAR_TYPE")
+ .appendField("final")
+ .appendField(new Blockly.FieldCheckbox("FALSE"), "IS_FINAL");
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#546E7A");
+ this.setTooltip("Creates a new variable.");
+ this.setHelpUrl("");
+ },
+
+ validateAndRenameVariable: function (newName) {
+ const workspace = Blockly.getMainWorkspace();
+ if (!workspace) return newName;
+
+ const currentName = this.getFieldValue('VAR_NAME');
+ const varType = this.getFieldValue('VAR_TYPE');
+
+ // Se o nome não mudou, não faça nada
+ if (currentName === newName) {
+ return newName;
+ }
+
+ const existingVariable = workspace.getVariable(currentName, varType);
+
+ // Se a variável atual não existir, crie uma nova
+ if (!existingVariable) {
+ console.warn(`Variable "${currentName}" does not exist. Creating a new one.`);
+ workspace.createVariable(newName, varType);
+ this.updateAllReferences(newName); // Atualiza referências no workspace
+ return newName;
+ }
+
+ // Verifica se já existe uma variável com o novo nome
+ const conflictingVariable = workspace.getVariable(newName, varType);
+ if (conflictingVariable) {
+ console.warn(`Variable "${newName}" already exists. Cannot rename.`);
+ return currentName; // Reverte para o nome anterior
+ }
+
+ // Renomeia a variável no workspace
+ workspace.renameVariableById(existingVariable.getId(), newName);
+ this.updateAllReferences(newName); // Atualiza referências no workspace
+
+ return newName;
+ },
+ updateAllReferences: function (variableId, newName) {
+ const workspace = Blockly.getMainWorkspace();
+
+ // Atualiza todos os blocos que utilizam a variável pelo ID
+ workspace.getAllBlocks().forEach(block => {
+ if (block.type === 'set_variable' || block.type === 'get_variable') {
+ const variableField = block.getField('VAR_NAME');
+ if (variableField && variableField.getValue() === variableId) {
+ variableField.setValue(variableId); // Atualiza o ID
+ }
+ if (typeof block.updateVariableDropdown === 'function') {
+ block.updateVariableDropdown(); // Atualiza os dropdowns
+ }
+ }
+ });
+ },
+ onchange: function (event) {
+ if (event.type === Blockly.Events.BLOCK_CREATE || Blockly.Events.BLOCK_CHANGE) {
+ const workspace = Blockly.getMainWorkspace();
+ const varName = this.getFieldValue('VAR_NAME');
+ const varType = this.getFieldValue('VAR_TYPE');
+
+ // Verifica se a variável já existe
+ const existingVariable = workspace.getVariable(varName, varType);
+
+ if (!existingVariable) {
+ // Cria a variável apenas se não existir
+ workspace.createVariable(varName, varType);
+ }
+ }
+ },
+};
+
+
+Blockly.JavaScript['create_variable'] = function(block) {
+ const varName = block.getFieldValue('VAR_NAME');
+ const varType = block.getFieldValue('VAR_TYPE');
+ const isFinal = block.getFieldValue('IS_FINAL') === 'TRUE' ? 'final ' : '';
+ return `${isFinal}${varType} ${varName};\n`;
+};
+
+// Atribuição de valor
+Blockly.Blocks['set_variable'] = {
+ init: function () {
+ this.appendDummyInput()
+ .appendField("Set Variable")
+ .appendField(new Blockly.FieldDropdown(this.getVariableOptions.bind(this)), "VAR_NAME")
+ .appendField("=");
+ this.appendValueInput("VALUE")
+ .setCheck(null);
+ this.setPreviousStatement(true, null);
+ this.setNextStatement(true, null);
+ this.setColour("#546E7A");
+ this.setTooltip("Sets a value to an existing variable.");
+ this.setHelpUrl("");
+ },
+
+ getVariableOptions: function () {
+ const workspace = Blockly.getMainWorkspace();
+ const variables = workspace.getAllVariables();
+ return variables.length > 0
+ ? variables.map(v => [v.name, v.getId()]) // Nome e ID da variável
+ : [["No Variables", ""]];
+ },
+
+ updateVariableDropdown: function () {
+ const variableField = this.getField("VAR_NAME");
+ const currentValue = variableField.getValue(); // Obtém o ID atual selecionado
+ const options = this.getVariableOptions(); // Obtém as opções atualizadas
+
+ // Atualiza as opções do dropdown
+ variableField.menuGenerator_ = options;
+
+ // Verifica se o valor atual ainda é válido
+ const isValidOption = options.some(([, id]) => id === currentValue);
+
+ if (!isValidOption && options.length > 0) {
+ // Define o valor para a primeira variável disponível
+ variableField.setValue(options[0][1]);
+ } else if (isValidOption) {
+ // Mantém o valor atual selecionado
+ variableField.setValue(currentValue);
+ }
+ },
+};
+
+Blockly.JavaScript['set_variable'] = function (block) {
+ const varId = block.getFieldValue('VAR_NAME');
+ const workspace = Blockly.getMainWorkspace();
+ const variable = workspace.getVariableById(varId);
+ const varName = variable ? variable.name : "unknown";
+ const value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC) || 'null';
+ return `${varName} = ${value};\n`;
+};
+
+// Obter valor de variável
+Blockly.Blocks['get_variable'] = {
+ init: function () {
+ this.appendDummyInput()
+ .appendField("Get Variable")
+ .appendField(new Blockly.FieldDropdown(this.getVariableOptions.bind(this)), "VAR_NAME");
+ this.setOutput(true, null);
+ this.setColour("#546E7A");
+ this.setTooltip("Gets the value of a variable.");
+ this.setHelpUrl("");
+ },
+
+ getVariableOptions: function () {
+ const workspace = Blockly.getMainWorkspace();
+ const variables = workspace.getAllVariables();
+ return variables.length > 0
+ ? variables.map(v => [v.name, v.getId()]) // Nome e ID da variável
+ : [["No Variables", ""]];
+ },
+
+ updateVariableDropdown: function () {
+ const variableField = this.getField("VAR_NAME");
+ const currentValue = variableField.getValue(); // Obtém o ID atual selecionado
+ const options = this.getVariableOptions(); // Obtém as opções atualizadas
+
+ // Atualiza as opções do dropdown
+ variableField.menuGenerator_ = options;
+
+ // Verifica se o valor atual ainda é válido
+ const isValidOption = options.some(([, id]) => id === currentValue);
+
+ if (!isValidOption && options.length > 0) {
+ // Define o valor para a primeira variável disponível
+ variableField.setValue(options[0][1]);
+ } else if (isValidOption) {
+ // Mantém o valor atual selecionado
+ variableField.setValue(currentValue);
+ }
+ },
+
+};
+
+
+Blockly.JavaScript['get_variable'] = function (block) {
+ const varId = block.getFieldValue('VAR_NAME');
+ const workspace = Blockly.getMainWorkspace();
+ const variable = workspace.getVariableById(varId);
+ const varName = variable ? variable.name : "unknown";
+ return [`${varName}`, Blockly.JavaScript.ORDER_ATOMIC];
+};
+
+
+js/main.js:
+//js/main.js
+/*
+ * Author: Francisco Iago Lira Passos (iagolirapassos@gmail.com)
+ * New blocks in https://google.github.io/blockly-samples/examples/developer-tools/index.html
+ * MIT LICENSE
+ */
+// Variáveis globais
+let workspace = Blockly.getMainWorkspace();
+let currentLanguage = 'pt-br';
+let dependencies = []; // Array to store dependency URLs
+
+// Sistema de notificações
+function showNotification(message, type) {
+ let oldNotification = document.querySelector('.notification');
+ if (oldNotification) {
+ oldNotification.remove();
+ }
+
+ let notification = document.createElement('div');
+ notification.className = `notification ${type}`;
+ notification.textContent = message;
+
+ document.body.appendChild(notification);
+
+ setTimeout(() => {
+ notification.remove();
+ }, 3000);
+}
+
+
+
+// Funções para sallet e carregar blocos
+window.saveBlocks = function() {
+ try {
+ let xmlDom = Blockly.Xml.workspaceToDom(workspace);
+ let xmlText = Blockly.Xml.domToPrettyText(xmlDom);
+
+ let saveData = {
+ version: "1.0",
+ timestamp: new Date().toISOString(),
+ blocks: xmlText
+ };
+
+ let saveString = JSON.stringify(saveData, null, 2);
+ let blob = new Blob([saveString], {type: 'application/json'});
+ let a = document.createElement('a');
+ a.download = 'extension_blocks.json';
+ a.href = URL.createObjectURL(blob);
+ a.click();
+
+ showNotification(TRANSLATIONS[currentLanguage]['file_success'], 'success');
+ } catch (e) {
+ console.error('Erro ao sallet blocos:', e);
+ showNotification(TRANSLATIONS[currentLanguage]['file_error'], 'error');
+ }
+};
+
+window.loadBlocks = function() {
+ let input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.json';
+
+ input.onchange = function(e) {
+ let file = e.target.files[0];
+ let reader = new FileReader();
+
+ reader.onload = function(e) {
+ try {
+ let saveData = JSON.parse(e.target.result);
+
+ if (!saveData.version) {
+ throw new Error('Formato de arquivo inválido');
+ }
+
+ let xmlDom = Blockly.utils.xml.textToDom(saveData.blocks);
+ workspace.clear();
+ Blockly.Xml.domToWorkspace(xmlDom, workspace);
+
+ showNotification(TRANSLATIONS[currentLanguage]['file_success'], 'success');
+ } catch (err) {
+ console.error('Erro ao carregar blocos:', err);
+ showNotification(TRANSLATIONS[currentLanguage]['file_error'], 'error');
+ }
+ };
+
+ reader.readAsText(file);
+ };
+
+ input.click();
+};
+
+
+document.addEventListener('DOMContentLoaded', function() {
+ const themeSelect = document.getElementById("themeSelect");
+ const outputCode = document.getElementById('outputCode');
+ const blocklyDiv = document.getElementById('blocklyDiv');
+ const resizeBlocklyDiv = () => {
+ const tabContainer = document.querySelector('.tab-container');
+ const header = document.querySelector('.header');
+ const availableHeight = window.innerHeight - tabContainer.offsetHeight - header.offsetHeight;
+ blocklyDiv.style.height = `${availableHeight}px`;
+ };
+
+ // Redimensiona o editor de blocos ao carregar e ao redimensionar a janela
+ resizeBlocklyDiv();
+ window.addEventListener('resize', resizeBlocklyDiv);
+
+
+ // Configuração do Blockly
+ workspace = Blockly.inject('blocklyDiv', {
+ toolbox: document.getElementById('toolbox'),
+ grid: {
+ spacing: 20,
+ length: 3,
+ colour: '#ccc',
+ snap: true
+ },
+ zoom: {
+ controls: true,
+ wheel: true,
+ startScale: 1.0,
+ maxScale: 3,
+ minScale: 0.3,
+ scaleSpeed: 1.2
+ },
+ trashcan: true
+ });
+
+ // Configuração da mudança de idioma
+ const languageSelect = document.getElementById('languageSelect');
+ languageSelect.addEventListener('change', function() {
+ currentLanguage = this.value; // Atualiza a variável global
+ changeLanguage(this.value);
+ });
+
+ function changeLanguage(lang) {
+
+ // Atualiza as categorias do toolbox
+ document.querySelectorAll('[id^="cat_"]').forEach(category => {
+ const categoryId = category.id.replace('cat_', 'category_');
+ if (TRANSLATIONS[lang][categoryId]) {
+ category.setAttribute('name', TRANSLATIONS[lang][categoryId]);
+ }
+ });
+
+ // Atualiza textos da interface
+ document.getElementById('title').textContent = TRANSLATIONS[lang]['title'];
+ // Atualiza categorias do toolbox
+ document.getElementById('cat_config').setAttribute('name', TRANSLATIONS[lang]['cat_config']);
+ document.getElementById('cat_props').setAttribute('name', TRANSLATIONS[lang]['cat_props']);
+ document.getElementById('cat_methods').setAttribute('name', TRANSLATIONS[lang]['cat_methods']);
+
+ // Atualiza os botões da IDE
+ document.querySelector('.text-save').parentElement.title = TRANSLATIONS[lang]['save_blocks'];
+ document.querySelector('.text-load').parentElement.title = TRANSLATIONS[lang]['load_blocks'];
+ document.querySelector('.text-generate').textContent = TRANSLATIONS[lang]['generate_code'];
+ document.querySelector('.text-download').textContent = TRANSLATIONS[lang]['download_file'];
+
+ // Atualiza o Blockly
+ Blockly.setLocale(lang);
+ }
+
+ // Função para aplicar o tema
+ function applyTheme(theme) {
+ const body = document.body;
+
+ // Remove todas as classes de tema previamente aplicadas
+ body.classList.remove("theme-light", "theme-dark", "theme-github", "theme-monokai");
+
+ // Adiciona a nova classe de tema
+ body.classList.add(`theme-${theme}`);
+
+ // Estilos adicionais para blockly e categorias do toolbox
+ const blocklyDiv = document.getElementById("blocklyDiv");
+ if (blocklyDiv) {
+ blocklyDiv.classList.remove("theme-light", "theme-dark", "theme-github", "theme-monokai");
+ blocklyDiv.classList.add(`theme-${theme}`);
+ }
+
+ // Atualiza as cores das categorias do toolbox
+ const toolboxCategories = document.querySelectorAll("#toolbox category");
+ toolboxCategories.forEach(category => {
+ category.style.color = getComputedStyle(document.body).getPropertyValue("--category-text-color");
+ });
+
+ // Atualiza as cores do workspace
+ Blockly.getMainWorkspace().updateToolbox(document.getElementById("toolbox"));
+ }
+
+ // Adicionar evento ao seletor
+ themeSelect.addEventListener("change", (event) => {
+ applyTheme(event.target.value);
+ });
+
+ // Aplicar o tema padrão ao carregar
+ applyTheme(themeSelect.value);
+
+ // Funções de geração e download de código
+ window.generateJavaCode = function () {
+ try {
+ const outputElement = document.getElementById('outputCode');
+
+ // Obtém os blocos principais em ordem
+ const topBlocks = workspace.getTopBlocks(true);
+
+ // Inicializa o código gerado
+ let code = '';
+
+ // Itera sobre os blocos principais em ordem
+ for (const block of topBlocks) {
+ // Gera código para cada bloco e seus filhos
+ code += Blockly.JavaScript.blockToCode(block);
+ }
+
+ // Remove os espaços à esquerda da primeira linha
+ const lines = code.split('\n');
+ if (lines.length > 0) {
+ lines[0] = lines[0].trimStart();
+ }
+ code = lines.join('\n');
+
+ // Define o código no elemento de saída
+ outputElement.textContent = code;
+
+ // Usa Prism.js para realçar o código
+ if (typeof Prism !== 'undefined' && Prism.languages.java) {
+ Prism.highlightElement(outputElement);
+ }
+ } catch (error) {
+ console.error('Error generating code:', error);
+ const outputElement = document.getElementById('outputCode');
+ outputElement.textContent = 'Error generating code. Check your blocks.';
+ }
+ };
+
+ window.downloadCode = function () {
+ const outputElement = document.getElementById('outputCode');
+ const code = outputElement.textContent || outputElement.innerText;
+
+ if (!code) {
+ showNotification("No code generated. Please generate code first.", "error");
+ return;
+ }
+
+ try {
+ // Extração do package e nome da classe
+ const packageMatch = code.match(/package\s+([\w\.]+);/);
+ const classMatch = code.match(/public\s+class\s+(\w+)/);
+
+ if (!packageMatch || !classMatch) {
+ throw new Error("Invalid Java code. Could not extract package or class name.");
+ }
+
+ const packageName = packageMatch[1];
+ const className = classMatch[1];
+
+ // Criar o nome do arquivo
+ const fileName = `${className}.java`;
+
+ // Criação do arquivo Java para download
+ const blob = new Blob([code], { type: "text/java;charset=utf-8" });
+ const a = document.createElement("a");
+ a.download = fileName;
+ a.href = URL.createObjectURL(blob);
+ a.click();
+
+ showNotification(`Code downloaded as ${fileName}`, "success");
+ } catch (error) {
+ console.error("Error while downloading code:", error);
+ showNotification("Failed to download code. Check the console for details.", "error");
+ }
+ };
+
+
+ document.getElementById('blocklyDiv').addEventListener('dragover', handleDragOver);
+ document.getElementById('blocklyDiv').addEventListener('drop', handleFileDrop);
+
+ function handleDragOver(e) {
+ e.preventDefault();
+ }
+
+ function handleFileDrop(e) {
+ e.preventDefault();
+ let file = e.dataTransfer.files[0];
+ let reader = new FileReader();
+
+ reader.onload = function(event) {
+ let saveData = JSON.parse(event.target.result);
+ let xmlDom = Blockly.utils.xml.textToDom(saveData.blocks);
+ workspace.clear();
+ Blockly.Xml.domToWorkspace(xmlDom, workspace);
+ };
+
+ reader.readAsText(file);
+ }
+
+ workspace.addChangeListener(event => {
+ if (
+ event.type === Blockly.Events.BLOCK_CREATE || // Bloco criado
+ event.type === Blockly.Events.BLOCK_CHANGE || // Bloco modificado
+ event.type === Blockly.Events.BLOCK_DELETE || // Bloco deletado
+ event.type === Blockly.Events.BLOCK_MOVE // Bloco movido
+ ) {
+ generateJavaCode();
+ }
+ });
+
+
+ Blockly.getMainWorkspace().addChangeListener(event => {
+ if (event.type === Blockly.Events.VAR_RENAME ||
+ event.type === Blockly.Events.VAR_CREATE ||
+ event.type === Blockly.Events.VAR_DELETE) {
+
+ Blockly.getMainWorkspace().getAllBlocks().forEach(block => {
+ if (typeof block.updateVariableDropdown === 'function') {
+ block.updateVariableDropdown();
+ }
+ });
+ }
+ });
+
+
+
+ // Define o idioma inicial
+ changeLanguage('en');
+});
+
+
+function showTab(tabId) {
+ const tabs = document.querySelectorAll('.tab-content');
+ const buttons = document.querySelectorAll('.tab-button');
+
+ tabs.forEach(tab => tab.classList.remove('active'));
+ buttons.forEach(button => button.classList.remove('active'));
+
+ const activeTab = document.getElementById(tabId);
+ activeTab.classList.add('active');
+ document.querySelector(`.tab-button[onclick="showTab('${tabId}')"]`).classList.add('active');
+
+ // Highlight syntax if tab content uses Prism.js
+ if (tabId === 'manifestTab' || tabId === 'fastTab' || tabId === 'outputTab') {
+ Prism.highlightAllUnder(activeTab);
+ }
+}
+
+
+// Function to add a dependency
+function addDependency() {
+ const dependencyUrl = document.getElementById("dependencyUrl").value;
+ if (!dependencyUrl) {
+ showNotification("Please enter a valid URL.", "error");
+ return;
+ }
+ dependencies.push(dependencyUrl);
+
+ const list = document.getElementById("dependenciesList");
+ const listItem = document.createElement("li");
+ listItem.textContent = dependencyUrl;
+
+ // Add a remove button
+ const removeButton = document.createElement("button");
+ removeButton.textContent = "Remove";
+ removeButton.onclick = () => {
+ dependencies = dependencies.filter(url => url !== dependencyUrl);
+ list.removeChild(listItem);
+ };
+ listItem.appendChild(removeButton);
+
+ list.appendChild(listItem);
+ document.getElementById("dependencyUrl").value = ""; // Clear the input field
+}
+
+// Função para compilar extensão
+// Extend compileExtension to send dependencies to the server
+async function compileExtension() {
+ const progressModal = showProgressModal();
+
+ try {
+ // Get data from the other tabs
+ const outputElement = document.getElementById("outputCode");
+ const code = outputElement.textContent || outputElement.innerText;
+ const manifestContent = document.getElementById("androidManifest").value;
+ const fastYmlContent = document.getElementById("fastYml").value;
+
+ if (!code) {
+ showNotification("No code generated. Please generate code first.", "error");
+ progressModal.close();
+ return;
+ }
+
+ // Extract packageName and className
+ const packageMatch = code.match(/package\s+([\w\.]+);/);
+ const classMatch = code.match(/public\s+class\s+(\w+)/);
+
+ if (!classMatch) {
+ throw new Error("Invalid Java code. Could not extract class name.");
+ }
+
+ const className = classMatch[1];
+ const packageName = packageMatch ? packageMatch[1] : "com.example";
+
+ // Update AndroidManifest.xml packageName
+ const updatedManifestContent = manifestContent.replace(
+ /package="[^"]*"/,
+ `package="${packageName}"`
+ );
+
+ // Send data to the server, including dependencies
+ const response = await fetch("https://localhost:8080/compile", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ code,
+ className,
+ packageName,
+ androidManifest: updatedManifestContent,
+ fastYml: fastYmlContent,
+ dependencies: dependencies, // Send dependencies as part of the request
+ }),
+ });
+
+ if (!response.ok) {
+ const errorDetails = await response.json().catch(() => null);
+ const errorMessage = errorDetails?.error || "Unknown error occurred.";
+ throw new Error(`Failed to compile the extension: ${errorMessage}`);
+ }
+
+ const blob = await response.blob();
+ const downloadUrl = URL.createObjectURL(blob);
+ progressModal.updateWithDownload(downloadUrl, `${className}.aix`);
+
+ await cleanupProjectDirectory(className);
+ } catch (error) {
+ console.error("Error during compilation:", error);
+ showNotification(
+ "Compilation failed. Check the console for more details.",
+ "error"
+ );
+ progressModal.close();
+ }
+}
+
+function showProgressModal() {
+ const modal = document.createElement('div');
+ modal.className = 'modal';
+
+ modal.innerHTML = `
+
+
Please wait...
+
Your extension is being generated. This might take a few seconds.
+
+
+
+ `;
+
+ document.body.appendChild(modal);
+
+ const closeModalButton = modal.querySelector('.close-modal');
+ if (closeModalButton) {
+ closeModalButton.addEventListener('click', () => {
+ document.body.removeChild(modal);
+ });
+ }
+
+ // Method to update the modal with a download link
+ modal.updateWithDownload = (downloadUrl, fileName) => {
+ modal.innerHTML = `
+
+
Extension Compiled Successfully!
+
Your extension is ready for download:
+
Download ${fileName}
+
+
+ `;
+
+ const updatedCloseModalButton = modal.querySelector('.close-modal');
+ if (updatedCloseModalButton) {
+ updatedCloseModalButton.addEventListener('click', () => {
+ document.body.removeChild(modal);
+ });
+ }
+ };
+
+ // Method to close the modal
+ modal.close = () => {
+ document.body.removeChild(modal);
+ };
+
+ return modal;
+}
+
+
+async function cleanupProjectDirectory(className) {
+ try {
+ const response = await fetch(`https://localhost:8080/cleanup/${className}`, {
+ method: "DELETE",
+ });
+
+ if (!response.ok) {
+ console.error("Failed to cleanup project directory. Response status:", response.status);
+ } else {
+ console.log(`Project directory for ${className} cleaned up successfully.`);
+ }
+ } catch (error) {
+ console.error("Error during project directory cleanup:", error);
+ }
+}
+
+
+// Evento de clique no botão
+//document.querySelector('.button').addEventListener('click', compileExtension);
+
+
+js/translations.js:
+const TRANSLATIONS = {
+ 'en': {
+ // Interface
+ 'title': 'CodeCrafter AIX',
+ 'btn_generate': 'Generate Java Code',
+ 'btn_download': 'Download .java',
+ 'theme_light': 'Light Theme',
+ 'theme_dark': 'Dark Theme',
+ 'theme_github': 'GitHub Theme',
+ 'theme_monokai': 'Monokai Theme',
+
+ // Categories
+ 'cat_config': 'Configuration',
+ 'cat_props': 'Properties',
+ 'cat_methods': 'Methods',
+ 'cat_control': 'Control',
+ 'cat_lists': 'Lists',
+ 'cat_math': 'Math',
+ 'cat_text': 'Text',
+ 'cat_logic': 'Logic',
+ 'cat_dictionaries': 'Dictionaries',
+ 'cat_colors': 'Colors',
+ 'cat_variables': 'Variables',
+ 'cat_functions': 'Functions',
+
+
+ // Botões da IDE
+ 'save_blocks': 'Save Blocks',
+ 'load_blocks': 'Load Blocks',
+ 'generate_code': 'Generate Java Code',
+ 'download_file': 'Download .java',
+
+ // Categorias do Toolbox
+ 'category_config': 'Configuration',
+ 'category_imports': 'Imports',
+ 'category_props': 'Properties',
+ 'category_methods': 'Methods',
+ 'category_control': 'Control',
+ 'category_math': 'Math',
+ 'category_text': 'Text',
+ 'category_lists': 'Lists',
+ 'category_logic': 'Logic',
+ 'category_dictionaries': 'Dictionaries',
+ 'category_imported': 'Imported Classes',
+ 'category_object': 'Objects',
+ 'category_colors': 'Colors',
+ 'category_variables': 'Variables',
+ 'category_functions': 'Functions',
+
+ // Blocos
+ 'block_return': 'return',
+ 'block_extension': 'Create Extension',
+ 'block_constructor': 'Constructor',
+ 'block_package': 'Package',
+ 'block_import': 'Import',
+ 'block_property': 'Property',
+ 'block_designer': 'Designer Property',
+ 'block_method': 'Method',
+ 'block_event': 'Event',
+ 'block_parameter': 'Parameter',
+
+ // Common terms
+ 'type': 'type',
+ 'return': 'return',
+ 'name': 'name',
+ 'value': 'value',
+ 'parameter': 'parameter',
+ 'description': 'description',
+ 'implementation': 'implementation',
+ 'add_parameter': 'Add Parameter',
+ 'remove_parameter': 'Remove Parameter',
+ // Control blocks
+ 'controls_if': 'if',
+ 'controls_for': 'for',
+ 'controls_while': 'while',
+ 'controls_try': 'try',
+ 'controls_catch': 'catch',
+ 'controls_finally': 'finally',
+ 'controls_return': 'return',
+ 'controls_do': 'do',
+ 'controls_else': 'else',
+
+ // Math blocks
+ 'math_number': 'number',
+ 'math_operation': 'operation',
+ 'math_function': 'function',
+ 'math_random': 'random',
+ 'math_constant': 'constant',
+ 'math_add': 'add',
+ 'math_subtract': 'subtract',
+ 'math_multiply': 'multiply',
+ 'math_divide': 'divide',
+ 'math_modulo': 'modulo',
+ 'math_power': 'power',
+ 'math_abs': 'absolute',
+ 'math_sqrt': 'square root',
+ 'math_sin': 'sine',
+ 'math_cos': 'cosine',
+ 'math_tan': 'tangent',
+ 'math_round': 'round',
+ 'math_ceil': 'ceiling',
+ 'math_floor': 'floor',
+ 'math_random_int': 'random integer',
+ 'math_random_float': 'random decimal',
+ 'math_random_bool': 'random boolean',
+
+ // Text blocks
+ 'text_string': 'text',
+ 'text_join': 'join',
+ 'text_length': 'length',
+ 'text_uppercase': 'to uppercase',
+ 'text_lowercase': 'to lowercase',
+ 'text_trim': 'trim',
+ 'text_charAt': 'character at',
+ 'text_indexOf': 'find in text',
+
+ // List blocks
+ 'lists_create': 'create list',
+ 'lists_add': 'add',
+ 'lists_remove': 'remove',
+ 'lists_get': 'get',
+ 'lists_set': 'set',
+ 'lists_clear': 'clear',
+ 'lists_size': 'size',
+ 'lists_forEach': 'for each',
+
+ // Constructor blocks
+ 'constructor_default': 'default constructor',
+ 'constructor_custom': 'custom constructor',
+ 'constructor_param': 'constructor parameter',
+ 'constructor_super': 'super call',
+
+ // Import blocks
+ 'import_manual': 'manual import',
+ 'import_package': 'import package',
+ 'import_class': 'import class',
+
+ // Object blocks
+ 'object_create': 'create object',
+ 'object_get_field': 'get field',
+ 'object_set_field': 'set field',
+ 'object_call_method': 'call method',
+ 'object_is_null': 'is null',
+ 'object_cast': 'cast to',
+ 'object_instance_of': 'is instance of',
+
+ // File operations
+ 'file_save': 'Save Blocks',
+ 'file_load': 'Load Blocks',
+ 'file_success': 'Operation successful',
+ 'file_error': 'Error during operation',
+
+ // Tooltips
+ 'tooltip_save': 'Save current blocks to file',
+ 'tooltip_load': 'Load blocks from file',
+ 'tooltip_import': 'Add custom import',
+ 'tooltip_object': 'Create new object instance',
+ 'tooltip_method': 'Call method from imported class',
+
+ 'logic_compare': 'compare',
+ 'logic_compare_title': 'comparisons',
+ 'logic_compare_item_title': 'comparison',
+ 'logic_operation_and': 'and',
+ 'logic_operation_or': 'or',
+ 'logic_negate': 'not',
+ 'logic_boolean_true': 'true',
+ 'logic_boolean_false': 'false',
+ 'logic_null': 'null',
+
+ 'logic_operation_title': 'Logical Operation',
+ 'logic_operation_item_title': 'Boolean Input',
+ 'logic_operation_empty': '(empty)',
+ 'logic_operation_and': 'AND',
+ 'logic_operation_or': 'OR'
+ },
+ 'pt-br': {
+ // Interface
+ 'title': 'CodeCrafter AIX',
+ 'btn_generate': 'Gerar Código Java',
+ 'btn_download': 'Baixar .java',
+ 'theme_light': 'Tema Claro',
+ 'theme_dark': 'Tema Escuro',
+ 'theme_github': 'Tema GitHub',
+ 'theme_monokai': 'Tema Monokai',
+
+ // Categorias
+ 'cat_config': 'Configuração',
+ 'cat_props': 'Propriedades',
+ 'cat_methods': 'Métodos',
+ 'cat_control': 'Controle',
+ 'cat_lists': 'Listas',
+ 'cat_math': 'Matemática',
+ 'cat_text': 'Texto',
+ 'cat_logic': 'Lógica',
+ 'cat_dictionaries': 'Dicionários',
+ 'cat_colors': 'Cores',
+ 'cat_variables': 'Variáveis',
+ 'cat_functions': 'Funções',
+
+ // Botões da IDE
+ 'save_blocks': 'Salvar Blocos',
+ 'load_blocks': 'Carregar Blocos',
+ 'generate_code': 'Gerar Código Java',
+ 'download_file': 'Baixar .java',
+
+ // Categorias do Toolbox
+ 'category_config': 'Configuração',
+ 'category_imports': 'Importações',
+ 'category_props': 'Propriedades',
+ 'category_methods': 'Métodos',
+ 'category_control': 'Controle',
+ 'category_math': 'Matemática',
+ 'category_text': 'Texto',
+ 'category_lists': 'Listas',
+ 'category_logic': 'Lógica',
+ 'category_dictionaries': 'Dicionários',
+ 'category_imported': 'Classes Importadas',
+ 'category_object': 'Objetos',
+ 'category_colors': 'Cores',
+ 'category_variables': 'Variáveis',
+ 'category_functions': 'Funções',
+
+ // Blocos
+ 'block_return': 'retornar',
+ 'block_extension': 'Criar Extensão',
+ 'block_constructor': 'Construtor',
+ 'block_package': 'Pacote',
+ 'block_import': 'Importar',
+ 'block_property': 'Propriedade',
+ 'block_designer': 'Propriedade Designer',
+ 'block_method': 'Método',
+ 'block_event': 'Evento',
+ 'block_parameter': 'Parâmetro',
+
+ // Termos comuns
+ 'type': 'tipo',
+ 'return': 'retorno',
+ 'name': 'nome',
+ 'value': 'valor',
+ 'parameter': 'parâmetro',
+ 'description': 'descrição',
+ 'implementation': 'implementação',
+ 'add_parameter': 'Adicionar Parâmetro',
+ 'remove_parameter': 'Remover Parâmetro',
+ // Blocos de controle
+ 'controls_if': 'se',
+ 'controls_for': 'para',
+ 'controls_while': 'enquanto',
+ 'controls_try': 'tentar',
+ 'controls_catch': 'capturar',
+ 'controls_finally': 'finalmente',
+ 'controls_return': 'retornar',
+ 'controls_do': 'faça',
+ 'controls_else': 'senão',
+
+ // Blocos matemáticos
+ 'math_number': 'número',
+ 'math_operation': 'operação',
+ 'math_function': 'função',
+ 'math_random': 'aleatório',
+ 'math_constant': 'constante',
+ 'math_add': 'adicionar',
+ 'math_subtract': 'subtrair',
+ 'math_multiply': 'multiplicar',
+ 'math_divide': 'dividir',
+ 'math_modulo': 'módulo',
+ 'math_power': 'potência',
+ 'math_abs': 'absoluto',
+ 'math_sqrt': 'raiz quadrada',
+ 'math_sin': 'seno',
+ 'math_cos': 'cosseno',
+ 'math_tan': 'tangente',
+ 'math_round': 'arredondar',
+ 'math_ceil': 'teto',
+ 'math_floor': 'piso',
+ 'math_random_int': 'inteiro aleatório',
+ 'math_random_float': 'decimal aleatório',
+ 'math_random_bool': 'booleano aleatório',
+
+ // Blocos de texto
+ 'text_string': 'texto',
+ 'text_join': 'juntar',
+ 'text_length': 'comprimento',
+ 'text_uppercase': 'maiúsculas',
+ 'text_lowercase': 'minúsculas',
+ 'text_trim': 'remover espaços',
+ 'text_charAt': 'caractere na posição',
+ 'text_indexOf': 'encontrar no texto',
+
+ // Blocos de lista
+ 'lists_create': 'criar lista',
+ 'lists_add': 'adicionar',
+ 'lists_remove': 'remover',
+ 'lists_get': 'obter',
+ 'lists_set': 'definir',
+ 'lists_clear': 'limpar',
+ 'lists_size': 'tamanho',
+ 'lists_forEach': 'para cada',
+
+ // Blocos do construtor
+ 'constructor_default': 'construtor padrão',
+ 'constructor_custom': 'construtor personalizado',
+ 'constructor_param': 'parâmetro do construtor',
+ 'constructor_super': 'chamada super',
+
+ // Blocos de importação
+ 'import_manual': 'importação manual',
+ 'import_package': 'importar pacote',
+ 'import_class': 'importar classe',
+
+ // Blocos de objeto
+ 'object_create': 'criar objeto',
+ 'object_get_field': 'obter campo',
+ 'object_set_field': 'definir campo',
+ 'object_call_method': 'chamar método',
+ 'object_is_null': 'é nulo',
+ 'object_cast': 'converter para',
+ 'object_instance_of': 'é instância de',
+
+ // Operações de arquivo
+ 'file_save': 'Salvar Blocos',
+ 'file_load': 'Carregar Blocos',
+ 'file_success': 'Operação realizada com sucesso',
+ 'file_error': 'Erro durante a operação',
+
+ // Dicas de ferramentas
+ 'tooltip_save': 'Salvar blocos atuais em arquivo',
+ 'tooltip_load': 'Carregar blocos de um arquivo',
+ 'tooltip_import': 'Adicionar importação personalizada',
+ 'tooltip_object': 'Criar nova instância de objeto',
+ 'tooltip_method': 'Chamar método de classe importada',
+
+ 'logic_compare': 'compara',
+ 'logic_compare_title': 'comparações',
+ 'logic_compare_item_title': 'comparação',
+ 'logic_operation_and': 'e',
+ 'logic_operation_or': 'ou',
+ 'logic_negate': 'não',
+ 'logic_boolean_true': 'verdadeiro',
+ 'logic_boolean_false': 'falso',
+ 'logic_null': 'nulo',
+
+ 'logic_operation_title': 'Operação Lógica',
+ 'logic_operation_item_title': 'Entrada Booleana',
+ 'logic_operation_empty': '(vazio)',
+ 'logic_operation_and': 'E',
+ 'logic_operation_or': 'OU'
+
+ }
+};
+
+
+server/NodeServer/package.json:
+{
+ "name": "codecrafter-aix-server",
+ "version": "1.0.0",
+ "description": "Server for managing and compiling MIT App Inventor extensions with dependencies support.",
+ "main": "server.js",
+ "scripts": {
+ "start": "node server.js",
+ "dev": "nodemon server.js"
+ },
+ "dependencies": {
+ "axios": "^1.7.7",
+ "cors": "^2.8.5",
+ "express": "^4.18.2"
+ },
+ "devDependencies": {
+ "nodemon": "^3.1.7"
+ },
+ "author": "Your Name ",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+}
+
+
+server/NodeServer/server.js:
+const express = require("express");
+const fs = require("fs");
+const path = require("path");
+const https = require("https");
+const axios = require("axios");
+const { execSync } = require("child_process");
+
+const app = express();
+const PORT = 8080;
+
+// Determine the operating system and set the path for `fast.jar`
+const SYSTEM = process.platform;
+const FAST_JAR_PATH =
+ SYSTEM === "win32"
+ ? path.join(process.env.LOCALAPPDATA || "", "Fast", "fast.jar")
+ : path.join(require("os").homedir(), ".local/share/Fast/fast.jar");
+
+// Verify if `fast.jar` exists
+if (!fs.existsSync(FAST_JAR_PATH)) {
+ console.error(`Fast CLI not found at ${FAST_JAR_PATH}. Ensure it is installed.`);
+ process.exit(1);
+}
+
+// Temporary directory for storing project files
+const TEMP_DIR = path.join(__dirname, "temp");
+
+// Ensure the temporary directory exists
+const getOrCreateTempDir = () => {
+ if (!fs.existsSync(TEMP_DIR)) {
+ fs.mkdirSync(TEMP_DIR, { recursive: true });
+ }
+ return TEMP_DIR;
+};
+
+// Middleware
+app.use(express.json());
+app.use((req, res, next) => {
+ res.setHeader("Access-Control-Allow-Origin", "*");
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
+ res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+ next();
+});
+
+// Function to set up the directory structure for Fast CLI
+const createFastStructure = (baseDir, packageName, className, code, androidManifest, fastYml) => {
+ const assetsDir = path.join(baseDir, "assets");
+ const depsDir = path.join(baseDir, "deps");
+ const srcDir = path.join(baseDir, "src");
+ const packageDir = path.join(srcDir, packageName.replace(/\./g, path.sep));
+
+ // Create required directories
+ [assetsDir, depsDir, packageDir].forEach((dir) => {
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
+ });
+
+ // Create `fast.yml`
+ fs.writeFileSync(
+ path.join(baseDir, "fast.yml"),
+ fastYml ||
+ `# Fast CLI Configuration
+author: BosonsHiggs
+auto_version: true
+min_sdk: 7
+desugar_sources: false
+desugar_deps: false
+desugar_dex: true
+deannonate: true
+filter_mit_classes: false
+proguard: true
+R8: false
+`
+ );
+
+ // Create AndroidManifest.xml
+ fs.writeFileSync(
+ path.join(srcDir, "AndroidManifest.xml"),
+ androidManifest ||
+ `
+
+
+
+`
+ );
+
+ // Save the Java source file
+ fs.writeFileSync(path.join(packageDir, `${className}.java`), code);
+
+ // Create ProGuard rules
+ fs.writeFileSync(
+ path.join(srcDir, "proguard-rules.pro"),
+ `-repackageclasses ${packageName}.repacked\n`
+ );
+
+ return baseDir;
+};
+
+// Function to download dependencies
+const handleDependencies = async (depsDir, dependencies) => {
+ if (!Array.isArray(dependencies)) {
+ throw new Error("Invalid dependencies format. Must be an array of URLs.");
+ }
+
+ for (const url of dependencies) {
+ if (!url || typeof url !== "string") {
+ throw new Error(`Invalid dependency URL: ${url}`);
+ }
+
+ const name = path.basename(new URL(url).pathname); // Extrai o nome do arquivo da URL
+ const filePath = path.join(depsDir, name);
+
+ if (!fs.existsSync(filePath)) {
+ console.log(`Downloading dependency: ${name}`);
+ try {
+ const response = await axios.get(url, { responseType: "stream" });
+ const writer = fs.createWriteStream(filePath);
+ response.data.pipe(writer);
+
+ await new Promise((resolve, reject) => {
+ writer.on("finish", resolve);
+ writer.on("error", reject);
+ });
+
+ console.log(`✔ Downloaded dependency: ${name}`);
+ } catch (error) {
+ console.error(`Failed to download dependency: ${name} from ${url}`);
+ throw new Error(`Failed to download dependency: ${name} from ${url}`);
+ }
+ } else {
+ console.log(`Dependency already exists: ${name}`);
+ }
+ }
+};
+
+
+// Endpoint to compile an extension
+app.post("/compile", async (req, res) => {
+ try {
+ const {
+ code,
+ className = "TestExtension",
+ packageName = "com.example.testextension",
+ androidManifest,
+ fastYml,
+ dependencies = [], // Assume lista de URLs simples
+ } = req.body;
+
+ if (!code) {
+ return res.status(400).json({ error: "No code provided" });
+ }
+
+ const tempDir = getOrCreateTempDir();
+ const projectDir = path.join(tempDir, `${className}_project`);
+
+ // Remove diretório existente
+ if (fs.existsSync(projectDir)) {
+ fs.rmSync(projectDir, { recursive: true });
+ }
+
+ // Cria estrutura do projeto
+ createFastStructure(projectDir, packageName, className, code, androidManifest, fastYml);
+
+ // Processa dependências
+ const depsDir = path.join(projectDir, "deps");
+ if (dependencies.length > 0) {
+ await handleDependencies(depsDir, dependencies);
+ }
+
+ // Executa o Fast CLI
+ const command = `java -jar "${FAST_JAR_PATH}" build`;
+ try {
+ execSync(command, { cwd: projectDir, stdio: "inherit" });
+ } catch (error) {
+ return res.status(500).json({ error: "Compilation failed", details: error.message });
+ }
+
+ // Verifica se o arquivo .aix foi gerado
+ const aixFileName = `${packageName}.aix`;
+ const aixFilePath = path.join(projectDir, "out", aixFileName);
+
+ if (!fs.existsSync(aixFilePath)) {
+ return res.status(500).json({ error: "AIX file not found" });
+ }
+
+ // Envia o arquivo gerado
+ res.download(aixFilePath, `${className}.aix`);
+ } catch (error) {
+ console.error("Error during compilation:", error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+// Endpoint to clean up the project directory
+app.delete("/cleanup/:className", (req, res) => {
+ try {
+ const { className } = req.params;
+ const tempDir = getOrCreateTempDir();
+ const projectDir = path.join(tempDir, `${className}_project`);
+
+ if (fs.existsSync(projectDir)) {
+ fs.rmSync(projectDir, { recursive: true });
+ res.json({ message: `Project directory for ${className} cleaned up successfully.` });
+ } else {
+ res.status(404).json({ error: "Project directory not found." });
+ }
+ } catch (error) {
+ console.error("Error during cleanup:", error);
+ res.status(500).json({ error: error.message });
+ }
+});
+
+// Load HTTPS certificates
+const key = fs.readFileSync("localhost.key");
+const cert = fs.readFileSync("localhost.crt");
+
+// Start the HTTPS server
+https.createServer({ key, cert }, app).listen(PORT, () => {
+ console.log(`Server is running on https://localhost:${PORT}`);
+});
+
+
+server/PythonServer/server.py:
+from flask import Flask, request, jsonify, send_file
+from flask_cors import CORS
+import os
+import subprocess
+import shutil
+import platform
+import requests
+
+app = Flask(__name__)
+CORS(app, supports_credentials=True) # Enable Cross-Origin Resource Sharing
+
+# Determine the operating system and set the path for `fast.jar`
+SYSTEM = platform.system().lower()
+if SYSTEM == "windows":
+ # On Windows, Fast CLI is installed in %LOCALAPPDATA%\Fast
+ FAST_JAR_PATH = os.path.join(os.environ.get("LOCALAPPDATA", ""), "Fast", "fast.jar")
+else:
+ # On Linux/MacOS, Fast CLI is installed in ~/.local/share/Fast
+ FAST_JAR_PATH = os.path.expanduser("~/.local/share/Fast/fast.jar")
+
+# Check if `fast.jar` exists
+if not os.path.isfile(FAST_JAR_PATH):
+ raise FileNotFoundError(f"Fast CLI not found at {FAST_JAR_PATH}. Ensure it is installed correctly.")
+
+# Temporary directory for storing project files
+TEMP_DIR = os.path.join(os.getcwd(), "temp")
+
+# Create or return the temporary directory
+def get_or_create_temp_dir():
+ """
+ Ensures the temporary directory exists or creates it if missing.
+ Returns:
+ str: Path to the temporary directory.
+ """
+ if not os.path.exists(TEMP_DIR):
+ os.makedirs(TEMP_DIR, exist_ok=True)
+ return TEMP_DIR
+
+# Function to set up the directory structure for Fast CLI
+def create_fast_structure(base_dir, package_name, class_name, code):
+ """
+ Creates the necessary directory and file structure for Fast CLI.
+
+ Args:
+ base_dir (str): Base directory for the project.
+ package_name (str): Java package name for the extension.
+ class_name (str): Main class name for the extension.
+ code (str): Java code to be saved in the project.
+
+ Returns:
+ str: The base directory path.
+ """
+ assets_dir = os.path.join(base_dir, "assets")
+ deps_dir = os.path.join(base_dir, "deps")
+ src_dir = os.path.join(base_dir, "src")
+ package_dir = os.path.join(src_dir, package_name.replace(".", os.sep))
+
+ # Create required directories
+ os.makedirs(assets_dir, exist_ok=True)
+ os.makedirs(deps_dir, exist_ok=True)
+ os.makedirs(package_dir, exist_ok=True)
+
+ # Create `fast.yml` configuration file
+ with open(os.path.join(base_dir, "fast.yml"), "w") as f:
+ f.write("""\
+# The name of the extension developer
+author: BosonsHiggs
+
+# If enabled, the version number of every component will be increased automatically.
+auto_version: true
+
+# The minimum Android SDK level your extension supports. Minimum SDK defined in
+# AndroidManifest.xml or @DesignerComponent are ignored, you should always define it here.
+min_sdk: 7
+
+# If enabled, Kotlin Standard Libraries (V1.9.24) will be included with the extension.
+# If you want to add specific Kotlin Standard Libraries so disable it.
+kotlin: false
+
+# If enabled, you will be able to use Java 8 language features in your extension source code.
+# When you use .kt classes, by default Fast will desugar sources.
+desugar_sources: false
+
+# Enable it, if any of your dependencies use Java 8 language features.
+# If kotlin is enabled, by default Fast will desugar dependencies.
+desugar_deps: false
+
+# If enabled, the D8 tool will generate desugared (classes.jar) classes.dex
+desugar_dex: true
+
+# If enabled, @annotations will be not present in built extension.
+deannonate: true
+
+# If enabled, matching classes provided by MIT will not be included in the built extension.
+filter_mit_classes: false
+
+# If enabled, it will optimizes the extension with ProGuard.
+proguard: true
+
+# If enabled, R8 will be used instead of ProGuard and D8 dexer.
+# NOTE: It's an experimental feature.
+R8: false
+
+# Extension dependencies (JAR) [Should be present into deps directory]
+# dependencies:
+# - mylibrary.jar
+
+# Extension assets. [Should be present into assets directory]
+#assets:
+#- my-awesome-asset.anything
+
+""")
+
+ # Create AndroidManifest.xml
+ with open(os.path.join(src_dir, "AndroidManifest.xml"), "w") as f:
+ f.write(f'''\
+
+
+
+
+
+
+
+
+
+
+
+
+
+''')
+
+ # Save the Java source file
+ with open(os.path.join(package_dir, f"{class_name}.java"), "w") as f:
+ f.write(code)
+
+ # Create ProGuard rules file
+ with open(os.path.join(src_dir, "proguard-rules.pro"), "w") as f:
+ f.write(f"""\
+-repackageclasses {package_name}.repacked
+""")
+
+ return base_dir
+
+@app.after_request
+def add_cors_headers(response):
+ response.headers["Access-Control-Allow-Private-Network"] = "true"
+ response.headers["Access-Control-Allow-Origin"] = "*" # Ajuste conforme necessário
+ response.headers["Access-Control-Allow-Methods"] = "GET, POST, DELETE, OPTIONS"
+ response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
+ return response
+
+# Endpoint to compile an extension
+@app.route("/compile", methods=["POST"])
+def compile_extension():
+ try:
+ data = request.json
+ code = data.get("code", "")
+ class_name = data.get("className", "TestExtension")
+ package_name = data.get("packageName", "com.example.testextension")
+ android_manifest = data.get("androidManifest", "")
+ fast_yml = data.get("fastYml", "")
+ dependencies = data.get("dependencies", []) # New field for dependencies
+
+ if not code:
+ return jsonify({"error": "No code provided"}), 400
+
+ temp_dir = get_or_create_temp_dir()
+ project_dir = os.path.join(temp_dir, f"{class_name}_project")
+ if os.path.exists(project_dir):
+ shutil.rmtree(project_dir)
+ os.makedirs(project_dir, exist_ok=True)
+
+ # Save project files
+ create_fast_structure(project_dir, package_name, class_name, code)
+ with open(os.path.join(project_dir, "src", "AndroidManifest.xml"), "w") as f:
+ f.write(android_manifest)
+ with open(os.path.join(project_dir, "fast.yml"), "w") as f:
+ f.write(fast_yml)
+
+ # Download dependencies
+ deps_dir = os.path.join(project_dir, "deps")
+ for url in dependencies:
+ file_name = os.path.basename(url)
+ file_path = os.path.join(deps_dir, file_name)
+ response = requests.get(url, stream=True)
+ if response.status_code == 200:
+ with open(file_path, "wb") as f:
+ f.write(response.content)
+ else:
+ return jsonify({"error": f"Failed to download dependency: {url}"}), 500
+
+ # Compile the extension
+ command = ["java", "-jar", FAST_JAR_PATH, "build"]
+ result = subprocess.run(
+ command,
+ cwd=project_dir,
+ capture_output=True,
+ text=True,
+ )
+
+ if result.returncode != 0:
+ return jsonify({"error": "Compilation failed", "details": result.stderr}), 500
+
+ aix_file_name = f"{package_name}.aix"
+ aix_file_path = os.path.join(project_dir, "out", aix_file_name)
+ if not os.path.exists(aix_file_path):
+ return jsonify({"error": "AIX file not found"}), 500
+
+ return send_file(aix_file_path, as_attachment=True, download_name=f"{class_name}.aix")
+ except Exception as e:
+ return jsonify({"error": str(e)}), 500
+
+@app.route("/cleanup/", methods=["DELETE"])
+def cleanup_project_directory(class_name):
+ """
+ Deletes the project directory for a given class name.
+ """
+ try:
+ temp_dir = get_or_create_temp_dir()
+ project_dir = os.path.join(temp_dir, f"{class_name}_project")
+
+ if os.path.exists(project_dir):
+ shutil.rmtree(project_dir)
+ print(f"Deleted project directory: {project_dir}")
+ return jsonify({"message": f"Project directory for {class_name} cleaned up successfully."}), 200
+ else:
+ return jsonify({"error": "Project directory not found."}), 404
+ except Exception as e:
+ print("Error during project directory cleanup:", str(e))
+ return jsonify({"error": str(e)}), 500
+
+
+if __name__ == "__main__":
+ # Run the server with HTTPS
+ app.run(host="0.0.0.0", port=8080, ssl_context=("localhost.crt", "localhost.key"))
+
+
diff --git a/css/blockly.css b/css/blockly.css
index c5cf749..105442d 100644
--- a/css/blockly.css
+++ b/css/blockly.css
@@ -1,9 +1,43 @@
-/* Estilo do Blockly e das abas */
+html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+}
+
+[id^="blocklyDivHelper"] {
+ height: calc(100vh - 120px) !important;
+ width: 100% !important;
+ position: relative !important;
+}
+
#blocklyDiv {
- flex: 1;
- overflow: hidden; /* Sem scrolls */
+ height: calc(100vh - 120px) !important; /* Ajuste o valor baseado na altura do header e tab-container */
+ width: 100% !important;
+ position: absolute;
+}
+
+
+.tab-content.active {
+ display: flex;
+ flex-direction: column;
+}
+
+#ideTab {
+ padding: 0;
+}
+
+.helper-file-name-input {
+ margin: 10px;
+ padding: 5px;
}
+/* Mantendo seus estilos existentes */
.tab-container {
width: 100%;
display: flex;
@@ -32,11 +66,12 @@
}
.tab-content.active {
- display: flex;
+ display: block;
flex-direction: column;
}
-.tab-content textarea, .tab-content pre {
+.tab-content textarea,
+.tab-content pre {
flex: 1;
resize: none;
overflow: auto;
@@ -48,3 +83,47 @@
border: 1px solid #ccc;
background-color: #f9f9f9;
}
+
+/* Adicione/atualize estas regras no seu blockly.css */
+
+[id^="blocklyDivHelper"], #blocklyDiv {
+ height: calc(100vh - 120px) !important;
+ min-height: 300px !important;
+ width: 100% !important;
+ position: relative !important;
+ transition: height 0.3s ease-in-out;
+}
+
+.tab-content {
+ position: relative;
+ height: calc(100vh - 120px);
+ min-height: 300px;
+ overflow: hidden;
+ display: none;
+ transition: height 0.3s ease-in-out;
+}
+
+.blocklyWorkspace {
+ position: relative !important;
+}
+
+.blocklyToolboxDiv {
+ position: absolute !important;
+}
+
+/* Previne problemas de redimensionamento em tela cheia */
+:-webkit-full-screen [id^="blocklyDivHelper"],
+:-webkit-full-screen #blocklyDiv {
+ height: calc(100vh - 120px) !important;
+}
+
+:-moz-full-screen [id^="blocklyDivHelper"],
+:-moz-full-screen #blocklyDiv {
+ height: calc(100vh - 120px) !important;
+}
+
+:fullscreen [id^="blocklyDivHelper"],
+:fullscreen #blocklyDiv {
+ height: calc(100vh - 120px) !important;
+}
+
diff --git a/css/helpers.css b/css/helpers.css
new file mode 100644
index 0000000..2531547
--- /dev/null
+++ b/css/helpers.css
@@ -0,0 +1,88 @@
+/* Adicione ao seu CSS */
+.helper-container {
+ display: flex;
+ width: 100%;
+ height: calc(100vh - 120px);
+ position: relative;
+}
+
+.helper-workspace {
+ flex: 1;
+ min-width: 0; /* Permite que o flex-basis funcione corretamente */
+ position: relative;
+}
+
+.helper-code-preview {
+ width: 400px;
+ border-left: 1px solid #ccc;
+ display: flex;
+ flex-direction: column;
+ background: #2d2d2d;
+}
+
+.helper-code-header {
+ padding: 8px;
+ background: #1e1e1e;
+ border-bottom: 1px solid #3d3d3d;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.helper-code-title {
+ color: #fff;
+ font-size: 0.9em;
+ margin: 0;
+}
+
+.helper-code-toggle {
+ background: none;
+ border: none;
+ color: #fff;
+ cursor: pointer;
+ padding: 4px 8px;
+ font-size: 1.2em;
+ display: flex;
+ align-items: center;
+}
+
+.helper-code-toggle:hover {
+ color: #007bff;
+}
+
+.helper-code-content {
+ flex: 1;
+ overflow: auto;
+ padding: 0;
+ margin: 0;
+}
+
+.helper-code-content code {
+ font-family: 'Fira Code', monospace;
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+/* Adiciona transição suave ao redimensionar */
+.helper-workspace,
+.helper-code-preview {
+ transition: width 0.3s ease;
+}
+
+/* Estado collapsed do preview */
+.helper-code-preview.collapsed {
+ width: 30px;
+}
+
+.helper-code-preview.collapsed .helper-code-content,
+.helper-code-preview.collapsed .helper-code-title {
+ display: none;
+}
+
+.helper-code-preview.collapsed .helper-code-toggle {
+ writing-mode: vertical-rl;
+ text-orientation: mixed;
+ padding: 10px 0;
+ width: 30px;
+ justify-content: center;
+}
\ No newline at end of file
diff --git a/css/modals.css b/css/modals.css
new file mode 100644
index 0000000..908b877
--- /dev/null
+++ b/css/modals.css
@@ -0,0 +1,146 @@
+/* Adicione ao seu arquivo CSS */
+.helper-settings-button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 5px;
+ margin-left: 10px;
+ color: #666;
+ display: inline-flex;
+ align-items: center;
+}
+
+.helper-settings-button:hover {
+ color: #333;
+}
+
+.helper-modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+}
+
+.helper-modal.active {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.helper-modal-content {
+ background-color: white;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+ width: 400px;
+ max-width: 90%;
+}
+
+.helper-modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+}
+
+.helper-modal-title {
+ margin: 0;
+ font-size: 1.2em;
+}
+
+.helper-modal-close {
+ background: none;
+ border: none;
+ font-size: 1.5em;
+ cursor: pointer;
+ padding: 0 5px;
+}
+
+.helper-modal-body {
+ margin-bottom: 20px;
+}
+
+.helper-modal-input {
+ width: 100%;
+ padding: 8px;
+ margin-bottom: 15px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+.helper-modal-buttons {
+ display: flex;
+ gap: 10px;
+ justify-content: flex-end;
+}
+
+.helper-modal-button {
+ padding: 8px 15px;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ background-color: #007bff;
+ color: white;
+}
+
+.helper-modal-button:hover {
+ background-color: #0056b3;
+}
+
+.helper-modal-button.secondary {
+ background-color: #6c757d;
+}
+
+.helper-modal-button.secondary:hover {
+ background-color: #545b62;
+}
+
+/* Adicione ao seu CSS */
+.helper-tab-wrapper {
+ display: flex;
+ align-items: center;
+ position: relative;
+ margin-right: 2px;
+}
+
+.helper-close-button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 5px;
+ margin-left: 5px;
+ color: #666;
+ display: inline-flex;
+ align-items: center;
+ font-size: 14px;
+}
+
+.helper-close-button:hover {
+ color: #ff4444;
+}
+
+.helper-control-buttons {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+}
+
+/* Atualizar estilo do botão de configurações para manter consistência */
+.helper-settings-button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 5px;
+ margin-left: 5px;
+ color: #666;
+ display: inline-flex;
+ align-items: center;
+}
+
+.helper-settings-button:hover {
+ color: #007bff;
+}
\ No newline at end of file
diff --git a/index.html b/index.html
index 00fb42b..21dc2ae 100644
--- a/index.html
+++ b/index.html
@@ -26,6 +26,8 @@
+
+
@@ -49,6 +51,7 @@ MIT App Inventor Extension Builder
+