first commit

This commit is contained in:
dharm pimsen 2024-06-30 13:02:14 +07:00
commit 36541cd83c
14 changed files with 3120 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

View File

@ -0,0 +1,21 @@
official support
https://www.curseforge.com/minecraft/mc-mods/cccbridge
https://www.curseforge.com/minecraft/mc-mods/ccdbridge
https://www.curseforge.com/minecraft/mc-mods/sc-peripherals
https://www.curseforge.com/minecraft/mc-mods/more-red-x-cc-tweaked-compat
https://www.curseforge.com/minecraft/mc-mods/advanced-peripherals
https://www.curseforge.com/minecraft/mc-mods/plethora-peripherals
https://www.curseforge.com/minecraft/mc-mods/more-peripherals
https://www.curseforge.com/minecraft/mc-mods/toms-peripherals
https://www.curseforge.com/minecraft/texture-packs/create-computercraft
https://modrinth.com/mod/computer-cartographer
https://modrinth.com/mod/peripheralium
https://modrinth.com/mod/unlimitedperipheralworks
https://modrinth.com/datapack/ctov-advanced-peripheral-compat
https://modrinth.com/mod/digital-items-3
https://modrinth.com/mod/cloud-solutions
https://modrinth.com/mod/cc-vs
https://modrinth.com/mod/cc-shops
official support (slow x2)
https://modrinth.com/mod/some-peripherals/versions

2340
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "ccide",
"version": "1.0.0",
"description": "ComputerCraft mod virtual lua IDE",
"main": "index.js",
"scripts": {
"dev": "electron src/.",
"nodedev": "node src/."
},
"author": "DPSoftware Foundation",
"license": "GPL-3.0-or-later",
"devDependencies": {
"electron": "^31.1.0"
},
"dependencies": {
"blockly": "^11.1.1",
"bootstrap": "^5.3.3",
"electron-prompt": "^1.7.0",
"sweetalert2": "^11.12.1",
"xml2js": "^0.6.2",
"xmldom": "^0.6.0"
}
}

View File

@ -0,0 +1,32 @@
{
"custom_math_operation": {
"message0": "%1 %2 %3",
"args0": [
{
"type": "field_dropdown",
"name": "OPERATOR",
"options": [
["+", "ADD"],
["-", "SUBTRACT"],
["*", "MULTIPLY"],
["/", "DIVIDE"]
]
},
{
"type": "input_value",
"name": "NUM1",
"check": "Number"
},
{
"type": "input_value",
"name": "NUM2",
"check": "Number"
}
],
"inputsInline": true,
"output": "Number",
"colour": 230,
"tooltip": "Perform a mathematical operation",
"helpUrl": ""
}
}

View File

@ -0,0 +1,35 @@
const { luaGenerator } = require('blockly/lua');
// Check if luaGenerator.forBlock is defined and initialize if necessary-
if (!luaGenerator.forBlock) {
luaGenerator.forBlock = {};
}
// Define your custom block handler
luaGenerator.forBlock['custom_math_operation'] = function(block, generator) {
var operator = block.getFieldValue('OPERATOR');
var num1 = generator.valueToCode(block, 'NUM1', generator.ORDER_ATOMIC);
var num2 = generator.valueToCode(block, 'NUM2', generator.ORDER_ATOMIC);
var operatorSymbol = '';
switch (operator) {
case 'ADD':
operatorSymbol = '+';
break;
case 'SUBTRACT':
operatorSymbol = '-';
break;
case 'MULTIPLY':
operatorSymbol = '*';
break;
case 'DIVIDE':
operatorSymbol = '/';
break;
default:
operatorSymbol = '+';
break;
}
var code = `${num1} ${operatorSymbol} ${num2}`;
return [code, generator.ORDER_ATOMIC];
};

View File

@ -0,0 +1,8 @@
{
"name": "Test Peripheral",
"author": "DPSoftware Foundation",
"description": "Test Peripheral",
"version": "1.0.0",
"category": "Blockly Extensions",
"license": "GPL-3.0-or-later"
}

View File

@ -0,0 +1,25 @@
<xml id="toolbox" style="display: none;">
<category name="Custom Blocks" colour="270">
<!-- Define your custom blocks here -->
<block type="custom_math_operation">
<value name="NUM1">
<shadow type="math_number">
<field name="NUM">10</field> <!-- Default value -->
</shadow>
</value>
<value name="NUM2">
<shadow type="math_number">
<field name="NUM">5</field> <!-- Default value -->
</shadow>
</value>
</block>
<block type="another_block_type">
<value name="INPUT">
<shadow type="math_number">
<field name="NUM">5</field> <!-- Default value -->
</shadow>
</value>
</block>
</category>
</xml>

25
readme.md Normal file
View File

@ -0,0 +1,25 @@
# ComputerCraft IDE (ccIDE)
ccIDE is block based programming for ComputerCraft lua
## official support peripherals
| Peripheral | Status
|------------|--------
|[Advanced Peripherals](https://www.curseforge.com/minecraft/mc-mods/advanced-peripherals) | 🟨 Under Development
|[CC:C Bridge](https://www.curseforge.com/minecraft/mc-mods/cccbridge) | 🟥 Unsupport
|[CC:Destroy Bridge](https://www.curseforge.com/minecraft/mc-mods/ccdbridge) | 🟥 Unsupport
|[SwitchCraft Peripherals](https://www.curseforge.com/minecraft/mc-mods/sc-peripherals) | 🟥 Unsupport
|[More Red x CC:Tweaked Compat](https://www.curseforge.com/minecraft/mc-mods/more-red-x-cc-tweaked-compat) | 🟥 Unsupport
|[Plethora Peripherals](https://www.curseforge.com/minecraft/mc-mods/plethora-peripherals) | 🟥 Unsupport
|[More Peripherals](https://www.curseforge.com/minecraft/mc-mods/more-peripherals) | 🟥 Unsupport
|[Tom's Peripherals](https://www.curseforge.com/minecraft/mc-mods/toms-peripherals) | 🟥 Unsupport
|[Create: ComputerCraft](https://www.curseforge.com/minecraft/texture-packs/create-computercraft) | 🟥 Unsupport
|[Computer Cartographer](https://modrinth.com/mod/computer-cartographer) | 🟥 Unsupport
|[Peripheralium](https://modrinth.com/mod/peripheralium) | 🟥 Unsupport
|[Unlimited Peripheral Works](https://modrinth.com/mod/unlimitedperipheralworks) | 🟥 Unsupport
|[CTOV](https://modrinth.com/datapack/ctov-advanced-peripheral-compat) | 🟥 Unsupport
|[Digital Items 3](https://modrinth.com/mod/digital-items-3) | 🟥 Unsupport
|[SirEdvin's Cloud Solutions](https://modrinth.com/mod/cloud-solutions) | 🟥 Unsupport
|[CC: VS](https://modrinth.com/mod/cc-vs) | 🟥 Unsupport
|[CC Shops](https://modrinth.com/mod/cc-shops) | 🟥 Unsupport
|[CC Shops](https://modrinth.com/mod/cc-shops) | 🟥 Unsupport
|[Some Peripherals](https://modrinth.com/mod/some-peripherals) | 🟥 Unsupport

77
src/blocksmanager.js Normal file
View File

@ -0,0 +1,77 @@
const fs = require('fs');
const path = require('path');
const xml2js = require('xml2js');
const peripheralsfolder = path.join(__dirname, "../peripherals");
function mergeXml(originalXml, appendXml) {
// Remove <xml id="toolbox" style="display: none;"> and </xml> from appendXml
const cleanedAppendXml = appendXml.replace(/^<xml[^>]*>|<\/xml>$/g, '').trim();
// Find the closing </xml> tag in originalXml
const closingTag = '</xml>';
const index = originalXml.lastIndexOf(closingTag);
if (index === -1) {
console.error('Closing </xml> tag not found in original XML.');
return originalXml; // return original XML as is
}
// Prepare the modified XML with the insertion of cleanedAppendXml
const modifiedXml = originalXml.slice(0, index) + cleanedAppendXml + originalXml.slice(index + closingTag.length);
return modifiedXml;
}
function loadperipheral(workspace, originaltoolbar, peripherals) {
const filePath = path.join(peripheralsfolder, peripherals);
const jsonfilePath = path.join(filePath, "block_design.json");
const xmlfilePath = path.join(filePath, "toolbox.xml");
const generatorfilePath = path.join(filePath, "generator.js"); // Path to generator.js
// Load generator.js
// Load blocks from block_design.json
fs.readFile(jsonfilePath, 'utf-8', (err, data) => {
if (err) {
console.error('Error loading JSON file:', err);
return;
}
try {
const blocksJson = JSON.parse(data);
for (const blockId in blocksJson) {
if (blocksJson.hasOwnProperty(blockId)) {
Blockly.Blocks[blockId] = {
init: function() {
this.jsonInit(blocksJson[blockId]);
}
};
}
}
} catch (e) {
document.getElementById('statusMessage').textContent = 'Error parsing JSON file: ' + e;
return;
}
});
// Load and merge new toolbox XML
try {
const toolbar = fs.readFileSync(xmlfilePath, 'utf8');
const newxml = mergeXml(originaltoolbar, toolbar);
workspace.updateToolbox(newxml);
document.getElementById('statusMessage').textContent = `Loaded ${peripherals}`;
} catch (error) {
console.error('Error loading or merging XML:', error);
document.getElementById('statusMessage').textContent = `Can't Load ${peripherals}`;
}
try {
require(generatorfilePath); // This will execute generator.js if it's a Node.js module
} catch (error) {
console.error('Error loading generator.js:', error);
}
}
module.exports = {
loadperipheral
}

137
src/index.html Normal file
View File

@ -0,0 +1,137 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ccIDE</title>
<script>
const bootstrap = require('bootstrap')
</script>
</head>
<body>
<!-- Navigation bar -->
<!-- Navigation bar -->
<nav id="navbar">
<button class="navbar-button" title="Peripherals" style="background-color: #0087bd;">
<svg viewBox="0 0 24 24"><path d="M15 7v4h1v2h-3V5h2l-3-4-3 4h2v8H8v-2.07c.7-.37 1.2-1.08 1.2-1.93A2.2 2.2 0 0 0 7 6.8c-1.22 0-2.2.98-2.2 2.2 0 .85.5 1.56 1.2 1.93V13a2 2 0 0 0 2 2h3v3.05c-.71.36-1.2 1.1-1.2 1.95a2.2 2.2 0 0 0 2.2 2.2 2.2 2.2 0 0 0 2.2-2.2c0-.85-.49-1.59-1.2-1.95V15h3a2 2 0 0 0 2-2v-2h1V7h-4z" /></svg>
</button>
<button class="navbar-button" title="Run" style="background-color: #0087bd; margin-left: auto;" onclick="gencode()">
<svg viewBox="0 0 24 24"><path d="M8 5.14v14l11-7-11-7z" /></svg>
</button>
</nav>
<!-- Loading screen -->
<div id="loadingScreen">
<p>Loading...</p>
</div>
<!-- Blockly workspace container -->
<section id="blocklyContainer">
<div id="blocklyDiv"></div>
</section>
<!-- Status bar -->
<div id="statusBar">
<p id="statusMessage">Initializing...</p>
</div>
<style>
body {
background-color: #212121;
margin: 0;
display: flex;
flex-direction: column;
height: 100vh;
}
#navbar {
background-color: #2c3e50;
color: white;
display: flex;
padding: 15px;
gap: 10px;
}
.navbar-button {
background-color: #2c3e50;
color: white;
border: none;
border-radius: 50%; /* Make the button circular */
width: 40px; /* Adjust size as needed */
height: 40px; /* Adjust size as needed */
font-size: 1em;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.3s;
font-size: 1.5em; /* Adjust size as needed */
}
.navbar-button:hover {
background-color: #34495e; /* Darken color on hover */
}
[data-bs-toggle="tooltip"] {
position: relative; /* Ensure tooltips are positioned correctly */
cursor: pointer; /* Add cursor style for tooltip interaction */
}
#loadingScreen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #212121;
color: #ffffff;
display: flex; /* or display: block; */
align-items: center;
justify-content: center;
font-size: 2em;
z-index : 1;
}
#blocklyContainer {
position: relative;
width: 100%;
height: calc(100vh - 10vh); /* Adjust height to accommodate navbar and status bar */
display: none; /* Initially hide the Blockly container */
}
#blocklyDiv {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
color: #212121;
}
#statusBar {
height: 3vh; /* Set the height of the status bar */
background-color: #333;
color: #ffffff;
display: flex;
align-items: center;
padding: 0 10px;
box-sizing: border-box;
}
#statusMessage {
margin: 0;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
var tooltips = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltips.map(function (tooltip) {
return new bootstrap.Tooltip(tooltip);
});
});
</script>
<script src="virtualcode.js"></script>
</body>
</html>

51
src/index.js Normal file
View File

@ -0,0 +1,51 @@
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
const prompt = require('electron-prompt');
const ipc = ipcMain
app.whenReady().then(() => {
const win = new BrowserWindow({
width: 1300,
height: 740,
webPreferences: {
devTools: true,
nodeIntegration: true,
contextIsolation: false,
}
})
win.loadFile('index.html')
//win.openDevTools();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
ipc.on('prompt', (event, promptText, defaultValue) => {
try {
prompt({
title: "ccIDE",
label: promptText,
value: defaultValue,
inputAttrs: {
type: 'text',
required: true
},
type: 'input',
alwaysOnTop: true
})
.then((r) => {
if(r === null) {
event.returnValue = null
} else {
event.returnValue = r
}
})
.catch(console.error);
} catch (error) {
console.error('Error showing prompt dialog:', error);
event.returnValue = null; // or handle error appropriately
}
});
})

300
src/toolbox.xml Normal file
View File

@ -0,0 +1,300 @@
<xml id="toolbox" style="display: none;">
<category name="Logic" categorystyle="logic_category">
<block type="controls_if"></block>
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_negate"></block>
<block type="logic_boolean"></block>
<block type="logic_null"></block>
<block type="logic_ternary"></block>
</category>
<category name="Control" categorystyle="loop_category">
<block type="controls_repeat_ext" >
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="controls_whileUntil"></block>
<block type="controls_for">
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
<value name="BY">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="controls_forEach"></block>
<block type="controls_flow_statements"></block>
</category>
<category name="Math" categorystyle="math_category">
<block type="math_number" gap="32">
<field name="NUM">123</field>
</block>
<block type="math_arithmetic">
<value name="A">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="B">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="math_single">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">9</field>
</shadow>
</value>
</block>
<block type="math_trig">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">45</field>
</shadow>
</value>
</block>
<block type="math_constant"></block>
<block type="math_number_property">
<value name="NUMBER_TO_CHECK">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="math_round">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">3.1</field>
</shadow>
</value>
</block>
<block type="math_on_list"></block>
<block type="math_modulo">
<value name="DIVIDEND">
<shadow type="math_number">
<field name="NUM">64</field>
</shadow>
</value>
<value name="DIVISOR">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="math_constrain">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">50</field>
</shadow>
</value>
<value name="LOW">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="HIGH">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="math_random_int">
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="math_random_float"></block>
<block type="math_atan2">
<value name="X">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="Y">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
</category>
<category name="Text" categorystyle="text_category">
<block type="text"></block>
<block type="text_join"></block>
<block type="text_append">
<value name="TEXT">
<shadow type="text"></shadow>
</value>
</block>
<block type="text_length">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_isEmpty">
<value name="VALUE">
<shadow type="text">
<field name="TEXT"></field>
</shadow>
</value>
</block>
<block type="text_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
<value name="FIND">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_charAt">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
</block>
<block type="text_getSubstring">
<value name="STRING">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
</block>
<block type="text_changeCase">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_trim">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">Hello World!</field>
</shadow>
</value>
</block>
<block type="text_count">
<value name="SUB">
<shadow type="text"></shadow>
</value>
<value name="TEXT">
<shadow type="text"></shadow>
</value>
</block>
<block type="text_replace">
<value name="FROM">
<shadow type="text"></shadow>
</value>
<value name="TO">
<shadow type="text"></shadow>
</value>
<value name="TEXT">
<shadow type="text"></shadow>
</value>
</block>
<block type="text_reverse">
<value name="TEXT">
<shadow type="text"></shadow>
</value>
</block>
<label text="Input/Output:" web-class="ioLabel"></label>
<block type="text_print">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_prompt_ext">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
</category>
<category name="Lists" categorystyle="list_category">
<block type="lists_create_with">
<mutation items="0"></mutation>
</block>
<block type="lists_create_with"></block>
<block type="lists_repeat">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
<block type="lists_length"></block>
<block type="lists_isEmpty"></block>
<block type="lists_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_getIndex">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_setIndex">
<value name="LIST">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_getSublist">
<value name="LIST">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_split">
<value name="DELIM">
<shadow type="text">
<field name="TEXT">,</field>
</shadow>
</value>
</block>
<block type="lists_sort"></block>
<block type="lists_reverse"></block>
</category>
<sep></sep>
<category name="Variables" categorystyle="variable_category" custom="VARIABLE">
</category>
<category name="Functions" categorystyle="procedure_category" custom="PROCEDURE"></category>
<sep></sep>
</xml>

45
src/virtualcode.js Normal file
View File

@ -0,0 +1,45 @@
document.getElementById('statusMessage').textContent = "loading";
const fs = require('fs');
const path = require('path');
const { ipcRenderer } = require("electron");
const { loadperipheral } = require("./blocksmanager");
const { luaGenerator } = require('blockly/lua'); // Use require syntax for Blockly module
const ipc = ipcRenderer;
// override prompt command
window.prompt = function(promptText, defaultValue) {
return ipc.sendSync("prompt", promptText, defaultValue);
};
const Blockly = require('blockly');
const peripheralsfolder = path.join(__dirname, "../peripherals");
const originaltoolbar = fs.readFileSync(path.join(__dirname, "toolbox.xml"), 'utf8');
var workspace = Blockly.inject('blocklyDiv', {
toolbox: originaltoolbar,
trashcan: true,
grid: {
spacing: 20,
length: 3,
colour: '#ccc',
snap: true
}
});
loadperipheral(workspace, originaltoolbar, "test");
document.getElementById('statusMessage').textContent = "ready";
workspace.getToolbox().getFlyout().autoClose = false;
// Ensure Blockly container is shown after the workspace is injected
document.getElementById('loadingScreen').style.display = 'none';
document.getElementById('blocklyContainer').style.display = 'block';
function gencode() {
return luaGenerator.workspaceToCode(workspace);
}