update 1.0.2

add save load new and make code more readable not complex
This commit is contained in:
dharm pimsen 2024-07-12 17:40:45 +07:00
parent b5cf9c9b64
commit aa6d2199c4
16 changed files with 610 additions and 268 deletions

4
.gitignore vendored
View File

@ -1 +1,3 @@
node_modules/
node_modules/
.mcattributes
package-lock.json

14
blocks/test/index.json Normal file
View File

@ -0,0 +1,14 @@
{
"name": "Test Peripheral",
"author": "DPSoftware Foundation",
"description": "Test Peripheral",
"version": "1.0.0",
"category": "Blockly Extensions",
"keyword": "test",
"license": "GPL-3.0-or-later",
"peripherals": false,
"library": false,
"turtle": false,
"system": true,
"require_network": false
}

BIN
ccIDE Defines.xlsx Normal file

Binary file not shown.

Binary file not shown.

221
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "ccide",
"version": "1.0.0",
"version": "1.0.2",
"description": "ComputerCraft mod virtual lua IDE",
"main": "index.js",
"scripts": {
@ -13,6 +13,7 @@
"electron": "^31.1.0"
},
"dependencies": {
"@electron/remote": "^2.1.2",
"blockly": "^11.1.1",
"bootstrap": "^5.3.3",
"electron-prompt": "^1.7.0"

View File

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

View File

@ -1,7 +1,7 @@
const fs = require('fs');
const path = require('path');
const peripheralsfolder = path.join(__dirname, "../peripherals");
const peripheralsfolder = path.join(__dirname, "../blocks");
function mergeXml(originalXml, appendXml) {
// Remove <xml id="toolbox" style="display: none;"> and </xml> from appendXml
@ -22,7 +22,7 @@ function mergeXml(originalXml, appendXml) {
return modifiedXml;
}
function loadperipheral(workspace, originaltoolbar, peripherals) {
function loadperipheral(workspace, currenttoolbar, peripherals) {
const filePath = path.join(peripheralsfolder, peripherals);
const jsonfilePath = path.join(filePath, "block_design.json");
const xmlfilePath = path.join(filePath, "toolbox.xml");
@ -53,24 +53,24 @@ function loadperipheral(workspace, originaltoolbar, peripherals) {
});
// Load and merge new toolbox XML
try {
const toolbar = fs.readFileSync(xmlfilePath, 'utf8');
const newxml = mergeXml(originaltoolbar, toolbar);
workspace.updateToolbox(newxml);
const toolbar = fs.readFileSync(xmlfilePath, 'utf8');
const newxml = mergeXml(currenttoolbar, toolbar);
workspace.updateToolbox(newxml);
document.getElementById('statusMessage').textContent = `Loaded ${peripherals}`;
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);
}
return newxml;
}
module.exports = {
loadperipheral
}

75
src/codegen.js Normal file
View File

@ -0,0 +1,75 @@
function delay(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
const { luaGenerator } = require('blockly/lua'); // Use require syntax for Blockly module
const progress = document.getElementById("progress");
const circles = document.querySelectorAll(".circle");
let upcurrentActive = 1;
let uploadError = false; // Flag to track if there's an error
const uploadUpdateProgress = () => {
circles.forEach((circle, index) => {
if (index < upcurrentActive) {
circle.classList.add("active");
if (index === upcurrentActive - 1 && uploadError) {
circle.classList.add("error");
} else {
circle.classList.remove("error");
}
} else {
circle.classList.remove("active");
circle.classList.remove("error"); // Ensure no error class on inactive circles
}
});
if (uploadError) {
progress.classList.add("error");
} else {
const actives = document.querySelectorAll(".active");
progress.style.width = ((actives.length - 1) / (circles.length - 1)) * 100 + "%";
progress.classList.remove("error");
}
};
async function gencode() {
document.getElementById('upload-popup').style.display = 'block';
upcurrentActive = 1;
uploadError = false;
uploadUpdateProgress();
// compile/convert code
upcurrentActive++;
uploadUpdateProgress();
document.getElementById('upload-status').textContent = "Generating code";
try {
let code = luaGenerator.workspaceToCode(workspace);
console.log(code);
} catch (e) {
uploadError = true;
uploadUpdateProgress();
document.getElementById('upload-status').textContent = e;
return
}
// upload to computercraft with remote
document.getElementById('upload-status').textContent = "Uploading code to machine";
upcurrentActive++;
uploadUpdateProgress();
await delay(1000)
// execute with remote
document.getElementById('upload-status').textContent = "Executing code";
upcurrentActive++;
uploadUpdateProgress();
await delay(1000)
// done!
document.getElementById('upload-status').textContent = "Done!";
document.getElementById('upload-popup').style.animation = 'fadeOut 0.3s ease'; // Apply fade-out animation
setTimeout(function() {
document.getElementById('upload-popup').style.display = 'none'; // Hide popup after animation completes
document.getElementById('upload-popup').style.animation = ''; // Reset animation property
}, 300); // Adjust to match animation duration in milliseconds
}

View File

@ -9,22 +9,24 @@
</script>
</head>
<body>
<!-- Navigation bar -->
<!-- Loading screen -->
<div id="loadingScreen">
<p>Loading...</p>
</div>
<!-- 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="Library and Packages" style="background-color: #0087bd;">
<svg viewBox="0 0 24 24"><path d="M21 16.5c0 .38-.21.71-.53.88l-7.9 4.44c-.16.12-.36.18-.57.18-.21 0-.41-.06-.57-.18l-7.9-4.44A.991.991 0 0 1 3 16.5v-9c0-.38.21-.71.53-.88l7.9-4.44c.16-.12.36-.18.57-.18.21 0 .41.06.57.18l7.9 4.44c.32.17.53.5.53.88v9M12 4.15l-1.89 1.07L16 8.61l1.96-1.11L12 4.15M6.04 7.5 12 10.85l1.96-1.1-5.88-3.4L6.04 7.5M5 15.91l6 3.38v-6.71L5 9.21v6.7m14 0v-6.7l-6 3.37v6.71l6-3.38z" /></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>
@ -35,7 +37,52 @@
<p id="statusMessage">Initializing...</p>
</div>
<div class="popup" id="upload-popup">
<div class="popup-content">
<span class="close" id="uploadCloseBtn">&times;</span>
<div class="upload-progress-container" style="text-align: center;">
<div class="progress-container">
<div class="progress" id="progress"></div>
<div class="circle active">
<svg viewBox="0 0 24 24"><path d="M8 3a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2H3v2h1a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h2v-2H8v-5a2 2 0 0 0-2-2 2 2 0 0 0 2-2V5h2V3m6 0a2 2 0 0 1 2 2v4a2 2 0 0 0 2 2h1v2h-1a2 2 0 0 0-2 2v4a2 2 0 0 1-2 2h-2v-2h2v-5a2 2 0 0 1 2-2 2 2 0 0 1-2-2V5h-2V3h2z" /></svg>
</div>
<div class="circle">
<svg viewBox="0 0 24 24"><path d="m5.5 4.14-1 1.72L15 12 4.5 18.14l1 1.72L19 12 5.5 4.14z" /></svg>
</div>
<div class="circle">
<svg viewBox="0 0 24 24"><path d="M9 16v-6H5l7-7 7 7h-4v6H9m-4 4v-2h14v2H5z" /></svg>
</div>
<div class="circle">
<svg viewBox="0 0 24 24"><path d="m20 22-3.86-1.55c.7-1.53 1.2-3.11 1.51-4.72L20 22M7.86 20.45 4 22l2.35-6.27c.31 1.61.81 3.19 1.51 4.72M12 2s5 2 5 10c0 3.1-.75 5.75-1.67 7.83A2 2 0 0 1 13.5 21h-3a2 2 0 0 1-1.83-1.17C7.76 17.75 7 15.1 7 12c0-8 5-10 5-10m0 10c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" /></svg>
</div>
</div>
<p id="upload-status"></p>
</div>
</div>
</div>
<style>
#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: 9999;
visibility: visible; /* Change visibility to hidden */
}
:root {
--line-border-fill: #3498db;
--line-border-empty: #e0e0e0;
}
body {
background-color: #212121;
margin: 0;
@ -77,26 +124,12 @@
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 */
visibility: hidden; /* Initially hide the Blockly container */
}
#blocklyDiv {
@ -105,7 +138,6 @@
left: 0;
right: 0;
bottom: 0;
color: #212121;
}
#statusBar {
@ -121,17 +153,151 @@
#statusMessage {
margin: 0;
}
/* Styles for the upload-popup container */
.popup {
display: none; /* Hide initially */
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */
z-index: 999; /* Ensure it overlays other content */
overflow: auto;
}
/* Styles for the upload-popup content */
.popup-content {
background-color: #fefefe;
margin: 15% auto; /* Center the upload-popup vertically and horizontally */
padding: 20px;
border: 1px solid #888;
width: 80%; /* Adjust width as needed */
max-width: 600px; /* Max width for larger screens */
animation: fadeIn 0.3s ease; /* Fade-in animation */
}
/* Close button */
.close {
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
}
/* Fade-in animation */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Fade-out animation */
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.progress-container {
display: flex;
justify-content: space-between;
position: relative;
margin-bottom: 30px;
max-width: 100%;
width: 350px;
}
.progress-container::before {
content: "";
background-color: var(--line-border-empty);
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
height: 4px;
width: 100%;
z-index: 99; /* Change z-index to 1 or higher */
}
.progress {
background-color: var(--line-border-fill);
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
height: 4px;
width: 0%;
z-index: 101;
transition: 0.4s ease;
}
.progress.error {
background-color: red; /* Set to red color for error state */
}
.circle {
background-color: #fff;
color: #999;
border-radius: 50%;
height: 30px;
width: 30px;
display: flex;
align-items: center;
justify-content: center;
border: 3px solid var(--line-border-empty);
transition: 0.4s ease;
z-index: 101;
}
.circle.active {
border-color: var(--line-border-fill);
}
.circle.error {
border-color: red; /* Optionally, change border color to red */
color: white; /* Optionally, adjust text color for visibility */
}
</style>
<script>
var uploadpopup = document.getElementById('upload-popup');
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>
// Event listener to close the upload-popup with fade-out effect
document.getElementById('uploadCloseBtn').addEventListener('click', function() {
uploadpopup.style.animation = 'fadeOut 0.3s ease'; // Apply fade-out animation
setTimeout(function() {
uploadpopup.style.display = 'none'; // Hide popup after animation completes
uploadpopup.style.animation = ''; // Reset animation property
}, 300); // Adjust to match animation duration in milliseconds
});
// Close the upload-popup if the user clicks outside of it
window.addEventListener('click', function(event) {
if (event.target == uploadpopup) {
uploadpopup.style.animation = 'fadeOut 0.3s ease'; // Apply fade-out animation
setTimeout(function() {
uploadpopup.style.display = 'none'; // Hide popup after animation completes
uploadpopup.style.animation = ''; // Reset animation property
}, 300); // Adjust to match animation duration in milliseconds
}
});
</script>
<script src="virtualcode.js"></script>
<script src="codegen.js"></script>
</body>
</html>

View File

@ -1,14 +1,27 @@
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
const { app, BrowserWindow, ipcMain, Menu, dialog } = require('electron')
const fs = require('fs');
const prompt = require('electron-prompt');
const path = require('path');
const ipc = ipcMain
let currentprojectpath = null;
let currentprojectname = null;
let currentprojectopen = false
let appexiting = false
app.whenReady().then(() => {
currentprojectpath = null;
currentprojectname = null;
currentprojectopen = false;
appexiting = false;
const win = new BrowserWindow({
width: 1300,
height: 740,
width: 1280,
height: 720,
webPreferences: {
devTools: true,
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false,
}
})
@ -16,11 +29,118 @@ app.whenReady().then(() => {
win.loadFile('index.html')
//win.openDevTools();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
//app.on('activate', () => {
// if (BrowserWindow.getAllWindows().length === 0) {
// createWindow()
// }
//})
// Define a custom menu template
const menuTemplate = [
{
label: 'File',
submenu: [
{
label: 'New',
accelerator: 'CmdOrCtrl+N',
click: () => {
win.reload()
}
},
{
label: 'Open',
accelerator: 'CmdOrCtrl+O',
click: () => {
dialog.showOpenDialog(win, {
title: 'Open Project',
defaultPath: app.getPath('documents'),
filters: [
{ name: 'ComputerCraft Project', extensions: ['ccp'] }
],
properties: ['openFile']
}).then(result => {
if (!result.canceled) {
const filePath = result.filePaths[0];
fs.readFile(filePath, 'utf8', (err, json) => {
if (err) {
console.error('Error loading workspace:', err);
return;
}
win.webContents.send('load-workspace', json);
currentprojectpath = result.filePaths[0]
currentprojectname = path.basename(result.filePaths[0]);
currentprojectopen = true;
win.setTitle(`${currentprojectname} | ccIDE`)
});
}
}).catch(err => {
console.error('Error showing open dialog:', err);
});
}
},
{
label: 'Save',
accelerator: 'CmdOrCtrl+S',
click: () => {
win.webContents.send('save-workspace-request');
}
},
{
label: 'Save as',
accelerator: 'CmdOrCtrl+Shift+S',
click: () => {
currentprojectopen = false
win.webContents.send('save-workspace-request');
}
},
{ type: 'separator' },
{
label: 'Exit',
accelerator: 'CmdOrCtrl+Q',
click: () => {
app.quit();
}
}
]
},
{
label: 'Edit',
submenu: [
{ role: 'undo', accelerator: 'CmdOrCtrl+Z' },
{ role: 'redo', accelerator: 'CmdOrCtrl+Y' },
{ type: 'separator' },
{ role: 'cut', accelerator: 'CmdOrCtrl+X' },
{ role: 'copy', accelerator: 'CmdOrCtrl+C' },
{ role: 'paste', accelerator: 'CmdOrCtrl+V' }
]
},
{
label: 'View',
submenu: [
{ label: 'DevTools', accelerator: 'F12', click: () => {win.openDevTools()}},
]
},
{
label: 'Help',
submenu: [
{
label: 'About',
click: () => {
console.log('About clicked');
}
}
]
}
})
];
// Set the custom menu
const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
ipc.on('prompt', (event, promptText, defaultValue) => {
try {
@ -48,4 +168,91 @@ app.whenReady().then(() => {
event.returnValue = null; // or handle error appropriately
}
});
})
ipc.on('save-workspace', (event, json) => {
if (!currentprojectopen) {
dialog.showSaveDialog(win, {
title: 'Save Project',
defaultPath: path.join(app.getPath('documents'), 'untitled.ccp'),
filters: [
{ name: 'ComputerCraft Project', extensions: ['ccp'] }
]
}).then(result => {
if (!result.canceled) {
fs.writeFile(result.filePath, JSON.stringify(json), (err) => {
if (err) {
console.error('Error saving project:', err);
dialog.showErrorBox("Save Project Error", err.message)
win.webContents.send('workspace-saved', false);
} else {
currentprojectpath = result.filePath
currentprojectname = path.basename(result.filePath);
currentprojectopen = true;
win.webContents.send('workspace-saved', true);
win.setTitle(`${currentprojectname} | ccIDE`)
}
});
} else {
win.webContents.send('workspace-saved', false);
}
}).catch(err => {
console.error('Error showing save dialog:', err);
dialog.showErrorBox("Save Project Error", err.message)
win.webContents.send('workspace-saved', false);
});
} else {
fs.writeFile(currentprojectpath, JSON.stringify(json), (err) => {
if (err) {
console.error('Error saving project:', err);
dialog.showErrorBox("Save Project Error", err.message)
win.webContents.send('workspace-saved', false);
} else {
currentprojectopen = true;
win.webContents.send('workspace-saved', true);
win.setTitle(`${currentprojectname} | ccIDE`)
if (appexiting) {
app.quit();
}
}
});
}
});
ipc.on('workspace-notsave', (event) => {
win.setTitle(`${currentprojectname}* | ccIDE`)
})
/*
win.on('close', function(e){
win.show()
});
app.on("before-quit", function() {
if (currentprojectopen) {
const result = dialog.showMessageBoxSync({
type: 'question',
buttons: ['Save', 'Don\'t Save', 'Cancel'],
defaultId: 2,
title: 'Save Changes',
message: "Your project is not saved",
});
if (result === 1) {
win = null
} else if (result === 0) {
appexiting = true;
win.webContents.send('save-workspace-request');
}
} else {
win = null
}
})
*/
})

View File

@ -242,7 +242,7 @@
</value>
</block>
</category>
<category name="Lists">
<category name="Table">
<block type="lists_create_with">
<mutation items="0"></mutation>
</block>

View File

@ -1,40 +1,24 @@
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 fs = require('fs');
const path = require('path');
const { ipcRenderer } = require("electron");
const { loadperipheral } = require("./blocksmanager");
const Blockly = require('blockly');
const ipc = ipcRenderer;
let isprojectsaved = false;
let usedlibinproject = []
Blockly.utils.colour.setHsvSaturation(0.9)
const peripheralsfolder = path.join(__dirname, "../peripherals");
const originaltoolbar = fs.readFileSync(path.join(__dirname, "toolbox.xml"), 'utf8');
const sysmodulejson = fs.readFileSync(path.join(__dirname, "module_block_design.json"), 'utf8');
const blocksJson = JSON.parse(sysmodulejson);
for (const blockId in blocksJson) {
if (blocksJson.hasOwnProperty(blockId)) {
Blockly.Blocks[blockId] = {
init: function() {
this.jsonInit(blocksJson[blockId]);
}
};
}
}
let originaltoolbar = fs.readFileSync(path.join(__dirname, "toolbox.xml"), 'utf8');
var workspace = Blockly.inject('blocklyDiv', {
toolbox: originaltoolbar,
trashcan: true,
@ -46,17 +30,49 @@ var workspace = Blockly.inject('blocklyDiv', {
}
});
loadperipheral(workspace, originaltoolbar, "test");
document.getElementById('statusMessage').textContent = "ready";
originaltoolbar = loadperipheral(workspace, originaltoolbar, "test");
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';
// Save workspace
ipc.on('save-workspace-request', (event) => {
const state = Blockly.serialization.workspaces.save(workspace);
const data = {
"usedlibrary": usedlibinproject,
"content": state
}
ipc.send('save-workspace', data);
});
function gencode() {
return luaGenerator.workspaceToCode(workspace);
}
// Load workspace
ipc.on('load-workspace', (event, json) => {
if (json) {
data = JSON.parse(json)
usedlibinproject = data.usedlibrary
workspace.clear()
Blockly.serialization.workspaces.load(data.content, workspace);
isprojectsaved = true
}
})
workspace.addChangeListener(function(event) {
if ([Blockly.Events.UI,
Blockly.Events.VIEWPORT_CHANGE,
Blockly.Events.TOOLBOX_ITEM_SELECT]
.includes(event.type)) {
return; // Don't care about UI events.
}
if (isprojectsaved) {
isprojectsaved = false
ipc.send("workspace-notsave")
};
});
ipc.on('workspace-saved', (event, success) => {
isprojectsaved = success
});
// Ensure Blockly container is shown after the workspace is injected
document.getElementById('loadingScreen').style.visibility = 'hidden';
document.getElementById('blocklyContainer').style.visibility = 'visible';
document.getElementById('statusMessage').textContent = "ready";