Compare commits

...

4 Commits
1.5 ... main

Author SHA1 Message Date
2e855b9907 update ui and webpack 2025-03-08 14:37:59 +07:00
5200e86f7d update 1.6
support multiple upload to machine and make remote script to one file and support to run on startup
2024-11-24 14:33:49 +07:00
a5ac8c99c8 update something 2024-10-30 18:43:25 +07:00
4cad5389d3 more update to 1.6 2024-10-29 22:48:05 +07:00
22 changed files with 12136 additions and 479 deletions

2
.gitignore vendored
View File

@ -3,4 +3,4 @@ node_modules/
package-lock.json
dist/
dist_debug/
ccIDE.wheel
ccIDE.wheel

View File

@ -1,50 +0,0 @@
local ws = assert(http.websocket("ws://127.0.0.1:5133"))
print("connected to server")
local id
local isrunning = true
function exitcheck()
while true do
local event = os.pullEventRaw("terminate")
if event == "terminate" then
print("Exiting...")
isrunning = false
ws.close()
break
end
end
end
function main()
while isrunning do
print("ready")
local message, error = ws.receive()
if message then
print("Received message:", message)
if message == "ping" then
ws.send("pong")
elseif message == "sendcode" then
local file = io.open("main.lua", "w")
print("waiting for code")
local filedata, error = ws.receive()
file:write(filedata)
file:close()
elseif message == "runcode" then
id = multishell.launch({}, "main.lua")
multishell.setTitle(id, "Code")
multishell.setFocus(id)
elseif message == "exit" then
print("Exiting...")
break
end
else
print("WebSocket error:", error)
break
end
end
end
parallel.waitForAny(exitcheck, main)
print("Exited")

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/DPSoftware2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -218,7 +218,7 @@
"tooltip": "Send message formatted to player chat"
},
"advanced_peripherals_chatbox_send_toast_formatted_to_player": {
"message0": "Chatbox %1 toast send %2 and title %3 to player %4\nPrefix %5 Brackets %6 Bracket color %7 Range %8",
"message0": "Chatbox %1 toast send formatted json %2 and title %3 to player %4\nPrefix %5 Brackets %6 Bracket color %7 Range %8",
"args0": [
{
"type": "input_value",
@ -227,7 +227,7 @@
},
{
"type": "input_value",
"name": "TEXT",
"name": "JSON",
"check": "String"
},
{
@ -1508,5 +1508,301 @@
"nextStatement": null,
"colour": 200,
"tooltip": "Sets the redstone level output on the given side to the given power level. (0-15)"
},
"advanced_peripherals_mc_beacon_get_level": {
"message0": "Get level from beacon %1",
"args0": [
{
"type": "input_value",
"name": "BEACON",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the level of the Beacon."
},
"advanced_peripherals_mc_beacon_get_primary_effect": {
"message0": "Get primary effect from beacon %1",
"args0": [
{
"type": "input_value",
"name": "BEACON",
"check": "Peripheral"
}
],
"output": "String",
"colour": 200,
"tooltip": "Returns the registry name of the beacon's primary effect."
},
"advanced_peripherals_mc_beacon_get_secondary_effect": {
"message0": "Get secondary secondary effect from beacon %1",
"args0": [
{
"type": "input_value",
"name": "BEACON",
"check": "Peripheral"
}
],
"output": "String",
"colour": 200,
"tooltip": "Returns the registry name of the beacon's secondary effect."
},
"advanced_peripherals_mc_note_block_play_note": {
"message0": "Play current note sound at note block %1",
"args0": [
{
"type": "input_value",
"name": "NOTEBLOCK",
"check": "Peripheral"
}
],
"previousStatement": null,
"nextStatement": null,
"colour": 200,
"tooltip": "Plays the Note Block's current note sound."
},
"advanced_peripherals_mc_note_block_get_note": {
"message0": "Get current note from note block %1",
"args0": [
{
"type": "input_value",
"name": "NOTEBLOCK",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the index for the Note Block's current note. A number from 0 to 24."
},
"advanced_peripherals_mc_note_block_change_note_by": {
"message0": "Change note to %1 at note block %2",
"args0": [
{
"type": "input_value",
"name": "TO",
"check": "Number"
},
{
"type": "input_value",
"name": "NOTEBLOCK",
"check": "Peripheral"
}
],
"previousStatement": null,
"nextStatement": null,
"colour": 200,
"tooltip": "Changes the Note Block's note to the given note. note must be a number from 0 to 24."
},
"advanced_peripherals_mc_note_block_change_note": {
"message0": "Change note by 1 at note block %1",
"args0": [
{
"type": "input_value",
"name": "NOTEBLOCK",
"check": "Peripheral"
}
],
"previousStatement": null,
"nextStatement": null,
"colour": 200,
"tooltip": "Increments the Note Block's note to the next available note."
},
"advanced_peripherals_botania_flowers_get_mana": {
"message0": "Get mana from flower %1",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the amount of mana stored in the flower."
},
"advanced_peripherals_botania_flowers_get_max_mana": {
"message0": "Get max mana from flower %1",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the maximum amount of mana that the flower can hold."
},
"advanced_peripherals_botania_flowers_is_floating": {
"message0": "Is flower %1 floating",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the flower is a floating flower.",
"inputsInline": true
},
"advanced_peripherals_botania_flowers_is_on_enchanted_soil": {
"message0": "Is flower %1 on enchanted soil",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the flower is placed on enchanted soil.",
"inputsInline": true
},
"advanced_peripherals_botania_flowers_is_empty": {
"message0": "Is flower %1 empty",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Flower is empty.",
"inputsInline": true
},
"advanced_peripherals_botania_flowers_is_full": {
"message0": "Is flower %1 full",
"args0": [
{
"type": "input_value",
"name": "FLOWER",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Flower is full.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_get_mana": {
"message0": "Get mana from pool %1",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the amount of mana stored in the pool."
},
"advanced_peripherals_botania_mana_pool_get_max_mana": {
"message0": "Get max mana from pool %1",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the maximum amount of mana that the pool can hold."
},
"advanced_peripherals_botania_mana_pool_get_mana_needed": {
"message0": "Get amount of mana is pool %1 needed to fill full",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Number",
"colour": 200,
"tooltip": "Returns the amount of mana needed to fill the pool. Equivalent to getMaxMana() - getMana().",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_is_empty": {
"message0": "Is pool %1 empty",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Mana Pool is empty.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_is_full": {
"message0": "Is pool %1 full",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Mana Pool is full.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_can_charge_item": {
"message0": "Can pool %1 change item",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if mode of the Mana Pool is set to charge the items on it.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_has_items": {
"message0": "Is pool %1 has item",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Boolean",
"colour": 200,
"tooltip": "Returns true if the Mana Pool has at least one item on it.",
"inputsInline": true
},
"advanced_peripherals_botania_mana_pool_get_items": {
"message0": "get items from pool %1 ",
"args0": [
{
"type": "input_value",
"name": "POOL",
"check": "Peripheral"
}
],
"output": "Array",
"colour": 200,
"tooltip": "Returns a table with the items lying on the Mana Pool."
}
}

View File

@ -552,4 +552,139 @@ luaGenerator.forBlock['advanced_peripherals_redstone_integrator_set_analog_outpu
var integrator = generator.valueToCode(block, 'INTEGRATOR', generator.ORDER_ATOMIC);
return `${integrator}.setOutput("${side}", ${power})\n`;
};
// Generator for Minecraft: Beacon
luaGenerator.forBlock['advanced_peripherals_mc_beacon_get_level'] = function(block, generator) {
var beacon = generator.valueToCode(block, 'BEACON', generator.ORDER_ATOMIC);
return [`${beacon}.getLevel()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_mc_beacon_get_primary_effect'] = function(block, generator) {
var beacon = generator.valueToCode(block, 'BEACON', generator.ORDER_ATOMIC);
return [`${beacon}.getPrimaryEffect()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_mc_beacon_get_secondary_effect'] = function(block, generator) {
var beacon = generator.valueToCode(block, 'BEACON', generator.ORDER_ATOMIC);
return [`${beacon}.getSecondaryEffect()`, luaGenerator.ORDER_NONE];
};
// Generator for Minecraft: Note Block
luaGenerator.forBlock['advanced_peripherals_mc_note_block_play_note'] = function(block, generator) {
var noteblock = generator.valueToCode(block, 'NOTEBLOCK', generator.ORDER_ATOMIC);
return `${noteblock}.playNote()\n`;
};
luaGenerator.forBlock['advanced_peripherals_mc_note_block_get_note'] = function(block, generator) {
var noteblock = generator.valueToCode(block, 'NOTEBLOCK', generator.ORDER_ATOMIC);
return [`${beacon}.getNote()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_mc_note_block_change_note_by'] = function(block, generator) {
var to = generator.valueToCode(block, 'TO', generator.ORDER_ATOMIC);
var noteblock = generator.valueToCode(block, 'NOTEBLOCK', generator.ORDER_ATOMIC);
return `${noteblock}.changeNoteBy(${to})\n`;
};
luaGenerator.forBlock['advanced_peripherals_mc_note_block_change_note'] = function(block, generator) {
var noteblock = generator.valueToCode(block, 'NOTEBLOCK', generator.ORDER_ATOMIC);
return `${noteblock}.changeNote()\n`;
};
// Generator for Botania: Flowers
luaGenerator.forBlock['advanced_peripherals_botania_flowers_get_mana'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.getMana()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_get_max_mana'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.getMaxMana()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_is_floating'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.isFloating()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_is_on_enchanted_soil'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.isOnEnchantedSoil()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_is_empty'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.isEmpty()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_flowers_is_full'] = function(block, generator) {
var flower = generator.valueToCode(block, 'FLOWER', generator.ORDER_ATOMIC);
return [`${flower}.isFull()`, luaGenerator.ORDER_NONE];
};
// Generator for Botania: Mana Pool
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_get_mana'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.getMana()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_get_max_mana'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.getMaxMana()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_get_mana_needed'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.getManaNeeded()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_is_empty'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.isEmpty()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_is_full'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.isFull()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_can_charge_item'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.canChargeItem()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_has_items'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.hasItems()`, luaGenerator.ORDER_NONE];
};
luaGenerator.forBlock['advanced_peripherals_botania_mana_pool_get_items'] = function(block, generator) {
var pool = generator.valueToCode(block, 'POOL', generator.ORDER_ATOMIC);
return [`${pool}.getItems()`, luaGenerator.ORDER_NONE];
};

View File

@ -2,7 +2,7 @@
"name": "Advanced Peripherals",
"author": "DPSoftware Foundation",
"description": "Advanced Peripherals is a mod that adds many useful extensions for CC:Tweaked.",
"version": "1.0.0",
"version": "1.2",
"category": "Mod",
"keyword": "mod",
"license": "GPL-3.0-or-later",

View File

@ -45,7 +45,7 @@
<shadow type="logic_null"></shadow>
</value>
</block>
<block type="advanced_peripherals_chatbox_send_message_to_player">
<block type="advanced_peripherals_chatbox_send_toast_to_player">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">Hello, world!</field>
@ -107,6 +107,30 @@
<shadow type="logic_null"></shadow>
</value>
</block>
<block type="advanced_peripherals_chatbox_send_toast_formatted_to_player">
<value name="TITLE">
<shadow type="text">
<field name="TEXT">Hi</field>
</shadow>
</value>
<value name="PLAYER">
<shadow type="text">
<field name="TEXT">Player</field>
</shadow>
</value>
<value name="PREFIX">
<shadow type="logic_null"></shadow>
</value>
<value name="BRACKET">
<shadow type="logic_null"></shadow>
</value>
<value name="BRACKETCOLOR">
<shadow type="logic_null"></shadow>
</value>
<value name="RANGE">
<shadow type="logic_null"></shadow>
</value>
</block>
<label text="Energy Detector"></label>
<block type="advanced_peripherals_energy_detector_get_transfer_rate"></block>
@ -462,5 +486,41 @@
<label text="RS Bridge"></label>
<label text="Colony Integrator"></label>
<label text="Minecraft: Beacon"></label>
<block type="advanced_peripherals_mc_beacon_get_level"></block>
<block type="advanced_peripherals_mc_beacon_get_primary_effect"></block>
<block type="advanced_peripherals_mc_beacon_get_secondary_effect"></block>
<label text="Minecraft: Note Block"></label>
<block type="advanced_peripherals_mc_note_block_play_note"></block>
<block type="advanced_peripherals_mc_note_block_get_note"></block>
<block type="advanced_peripherals_mc_note_block_change_note_by">
<value name="TO">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="advanced_peripherals_mc_note_block_change_note"></block>
<label text="Botania: Flowers"></label>
<block type="advanced_peripherals_botania_flowers_get_mana"></block>
<block type="advanced_peripherals_botania_flowers_get_max_mana"></block>
<block type="advanced_peripherals_botania_flowers_is_floating"></block>
<block type="advanced_peripherals_botania_flowers_is_on_enchanted_soil"></block>
<block type="advanced_peripherals_botania_flowers_is_empty"></block>
<block type="advanced_peripherals_botania_flowers_is_full"></block>
<label text="Botania: Mana Pool"></label>
<block type="advanced_peripherals_botania_mana_pool_get_mana"></block>
<block type="advanced_peripherals_botania_mana_pool_get_max_mana"></block>
<block type="advanced_peripherals_botania_mana_pool_get_mana_needed"></block>
<block type="advanced_peripherals_botania_mana_pool_is_empty"></block>
<block type="advanced_peripherals_botania_mana_pool_is_full"></block>
<block type="advanced_peripherals_botania_mana_pool_can_charge_item"></block>
<block type="advanced_peripherals_botania_mana_pool_has_items"></block>
<block type="advanced_peripherals_botania_mana_pool_get_items"></block>
</category>
</xml>

View File

@ -6,14 +6,15 @@ const path = require('path');
const pino = require('pino')
const pretty = require('pino-pretty');
const https = require('node:https');
const LocalStorage = require('node-localstorage').LocalStorage
const ipc = ipcMain
const logger = pino(pretty())
const localStorage = new LocalStorage('.');
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
var ena_splash = process.env['CCIDE_ENABLE_MAIN_SPLASH'] == 'true';
var currentprojectpath = null;
var currentprojectname = null;
var currentprojectopen = false;
@ -111,46 +112,51 @@ app.whenReady().then(async () => {
logger.error('Error in update check:', error);
}
logger.info("Version in github: " + gitver)
logger.info("Current version: " + version)
logger.info("Version in github: " + gitver)
const normalizedAppVersion = normalizeVersion(version);
const normalizedReleaseVersion = normalizeVersion(gitver);
if (normalizedAppVersion >= normalizedReleaseVersion) {
logger.info("Software is up-to-date.");
if (!gitver) {
logger.error("Can't check update")
} else {
logger.info("A new update is available: " + gitver)
var is_not_skip_update = localStorage.getItem('skip_update_version') != normalizedAppVersion;
var is_ignore = localStorage.getItem('ignore_update');
if (is_ignore || is_not_skip_update) {
const result = dialog.showMessageBoxSync({
type: 'question',
buttons: ['Update', 'Ignore', 'Skip'],
defaultId: 0,
title: 'Update Available',
message: `A new version (${gitver}) is available. Do you want to update now?`,
detail: 'Click "Update" to go to the release page, "Ignore" to ignore this update, or "Skip" to skip this version.'
});
switch (result.response) {
case 0: // 'Update'
(async () => {
try {
const { default: open } = await import('open');
await open('https://github.com/DPSoftware-Foundation/ccIDE/releases/latest');
console.log('URL opened in default browser');
} catch (err) {
console.error('Error opening URL:', err);
}
})();
break;
case 1: // 'Ignore'
localStorage.setItem('ignore_update', true);
break;
case 2: // 'Skip'
localStorage.setItem('skip_update_version', normalizedReleaseVersion);
break;
const normalizedReleaseVersion = normalizeVersion(gitver);
if (normalizedAppVersion >= normalizedReleaseVersion) {
logger.info("Software is up-to-date.");
} else {
logger.info("A new update is available: " + gitver)
var is_not_skip_update = localStorage.getItem('skip_update_version') != normalizedAppVersion;
var is_ignore = localStorage.getItem('ignore_update');
if (is_ignore || is_not_skip_update) {
const result = dialog.showMessageBoxSync({
type: 'question',
buttons: ['Update', 'Ignore', 'Skip'],
defaultId: 0,
title: 'Update Available',
message: `A new version (${gitver}) is available. Do you want to update now?`,
detail: 'Click "Update" to go to the release page, "Ignore" to ignore this update, or "Skip" to skip this version.'
});
switch (result.response) {
case 0: // 'Update'
(async () => {
try {
const { default: open } = await import('open');
await open('https://github.com/DPSoftware-Foundation/ccIDE/releases/latest');
console.log('URL opened in default browser');
} catch (err) {
console.error('Error opening URL:', err);
}
})();
break;
case 1: // 'Ignore'
localStorage.setItem('ignore_update', true);
break;
case 2: // 'Skip'
localStorage.setItem('skip_update_version', normalizedReleaseVersion);
break;
}
}
}
}
@ -168,7 +174,7 @@ app.whenReady().then(async () => {
enableRemoteModule: true,
contextIsolation: false,
},
show: false,
show: ena_splash,
center: true,
})
@ -218,10 +224,13 @@ app.whenReady().then(async () => {
});
ipc.on('update-log-status', (event, status) => {
logger.info(status)
logger.info(status);
if (!appstarted) {
splash.webContents.send("change-status", status)
}
splash.webContents.send("change-status", status);
}
if (status == "Downloading blocks...") {
win.show();
}
});
//app.on('activate', () => {

10639
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,31 @@
{
"name": "ccide",
"version": "1.5",
"version": "1.6.0",
"description": "ComputerCraft mod virtual lua IDE",
"main": "index.js",
"main": "dist/main.js",
"scripts": {
"dev": "title ccIDE && electron .",
"build": "title building ccIDE && electron-packager . ccIDE --platform=win32 --arch=x64 --icon=assets/ccIDEIcon.ico --out=dist --overwrite && for /d %i in (dist\\ccIDE-*) do copy package.json %i\\",
"build_debug": "title building ccIDE (Debug) && electron-packager . ccIDE --platform=win32 --arch=x64 --icon=assets/ccIDEIcon.ico --out=dist_debug --overwrite --debug && for /d %i in (dist_debug\\ccIDE-*) do copy package.json %i\\"
"build_debug": "title building ccIDE (Debug) && electron-packager . ccIDE --platform=win32 --arch=x64 --icon=assets/ccIDEIcon.ico --out=dist_debug --overwrite --debug && for /d %i in (dist_debug\\ccIDE-*) do copy package.json %i\\",
"new_build": "webpack --config webpack.main.config.js && webpack --config webpack.renderer.config.js",
"dist": "electron-builder"
},
"author": "DPSoftware Foundation",
"license": "GPL-3.0-or-later",
"devDependencies": {
"electron": "^31.1.0"
"copy-webpack-plugin": "^12.0.2",
"electron": "^31.7.7",
"electron-builder": "^25.1.8",
"html-webpack-plugin": "^5.6.3",
"terser-webpack-plugin": "^5.3.11",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1",
"webpack-merge": "^6.0.1",
"webpack-node-externals": "^3.0.0"
},
"dependencies": {
"@blockly/plugin-workspace-search": "^9.0.5",
"@blockly/theme-dark": "^7.0.7",
"@electron/remote": "^2.1.2",
"blockly": "^11.1.1",
"bootstrap": "^5.3.3",
@ -23,6 +34,19 @@
"open": "^10.1.0",
"pino": "^9.3.2",
"pino-pretty": "^11.2.2",
"toastify-js": "^1.12.0",
"unzipper": "^0.12.3",
"xmldom": "^0.6.0"
},
"build": {
"appId": "dev.dpsoftware.ccide",
"productName": "ccIDE",
"files": [
"dist/**/*",
"node_modules/**/*"
],
"directories": {
"output": "release"
}
}
}

View File

@ -6,34 +6,18 @@ special thank for [ccblockly](https://github.com/Mirka1405/ccblockly) for idea
![Screenshot 2024-08-20 202018](https://github.com/user-attachments/assets/ba6c1ca6-ce91-41c9-b412-b1f3a8f2e735)
# Quick Start
1. install nodejs and git
2. git clone cd to this project
3. Install dependency use `npm install .`
1. install nodejs and git.
2. git clone and cd to this project .
3. Install dependency use `npm install .`.
4. To run this IDE use `npm run dev` or if you using windows you can use `run.bat` to run it.
5. Done!
## Install Remote code into computercraft
it very simple! to install Remote code.
### Run from URL
for advanced computer/pocket/turtle
```
wget run https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/advremote.lua
```
for non advance computer/pocket/turtle
```
wget run https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/remote.lua
```
### Download and Run
for advanced computer/pocket/turtle
```
wget https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/advremote.lua advremote.lua
advremote
```
for non advanced computer/pocket/turtle
```
wget https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/remote.lua remote.lua
remote
wget https://raw.githubusercontent.com/DPSoftware-Foundation/ccIDE/main/startup.lua
```
And restart the computer.
If error "Domain not permitted" try [this solution](https://github.com/cc-tweaked/CC-Tweaked/discussions/626#discussioncomment-241924).
## official support library, peripheral and module function
@ -150,7 +134,7 @@ https://github.com/user-attachments/assets/195231d4-8fd8-4101-8068-70bc038a5c4f
https://github.com/user-attachments/assets/8f114cfa-d87c-47d0-a670-a13dc975ab06
# For adapting in other project
This project is based for every block based IDE from DPSoftware Foundation
This project is for every block based IDE from DPSoftware Foundation.
# License
This project is licensed under the [GPL v3 License](https://github.com/DPSoftware-Foundation/ccIDE/blob/main/LICENSE).

View File

@ -1,48 +0,0 @@
local ws = assert(http.websocket("ws://127.0.0.1:5133"))
print("connected to server")
local id
local isrunning = true
function exitcheck()
while true do
local event = os.pullEventRaw("terminate")
if event == "terminate" then
print("Exiting...")
isrunning = false
ws.close()
break
end
end
end
function main()
while isrunning do
print("ready")
local message, error = ws.receive()
if message then
print("Received message:", message)
if message == "ping" then
ws.send("pong")
elseif message == "sendcode" then
local file = io.open("main.lua", "w")
print("waiting for code")
local filedata, error = ws.receive()
file:write(filedata)
file:close()
elseif message == "runcode" then
shell.run("main")
elseif message == "exit" then
print("Exiting...")
break
end
else
print("WebSocket error:", error)
break
end
end
end
parallel.waitForAny(exitcheck, main)
print("Exited")

View File

@ -1,11 +1,110 @@
const fs = require('fs');
const path = require('path');
const https = require('https');
const unzipper = require('unzipper');
const { DOMParser, XMLSerializer } = require('xmldom');
const peripheralsfolder = path.join(__dirname, "../blocks");
const fallbackImagePath = path.join(__dirname, '..', 'assets', 'noimagefallback.png'); // Path to fallback image
if (!fs.existsSync(peripheralsfolder)) {
// If it doesn't exist, create it
fs.mkdirSync(peripheralsfolder, { recursive: true });
console.log('Block Folder created');
}
let progressBar = document.getElementById('progressBarloading');
let blocks_url = "https://cdn.damp11113.xyz/file/zip/ccide/blockslastest.zip?dl=1"
function isBlocksFolderEmpty() {
try {
const files = fs.readdirSync(peripheralsfolder);
return files.length === 0; // Returns true if the folder is empty
} catch (err) {
console.error('Error reading folder:', err);
return false; // Return false or handle error as needed
}
}
async function downloadBlocks() {
try {
progressBar.style.display = 'inline-block';
// Create the output directory if it doesn't exist
if (!fs.existsSync(peripheralsfolder)) {
fs.mkdirSync(peripheralsfolder, { recursive: true });
}
console.log('Downloading blocks...');
const zipFilePath = path.join(peripheralsfolder, 'blocks.zip');
// Download the file as a Promise
await new Promise((resolve, reject) => {
https.get(blocks_url, (response) => {
if (response.statusCode !== 200) {
reject(new Error(`Download failed with status code ${response.statusCode}`));
return;
}
const totalBytes = parseInt(response.headers['content-length'], 10);
let downloadedBytes = 0;
const file = fs.createWriteStream(zipFilePath);
response.on('data', (chunk) => {
downloadedBytes += chunk.length;
const percentCompleted = Math.round((downloadedBytes / totalBytes) * 100);
progressBar.value = percentCompleted;
progressBar.innerText = percentCompleted + '%'; // Fallback text
process.stdout.write(`Download progress: ${percentCompleted}%\r`);
});
response.pipe(file);
file.on('finish', () => {
file.close();
console.log('\nFile downloaded successfully.');
resolve();
});
file.on('error', (error) => {
fs.unlinkSync(zipFilePath);
reject(error);
});
}).on('error', (error) => {
reject(error);
});
});
console.log('Unzipping file...');
// Unzip the file as a Promise
await new Promise((resolve, reject) => {
fs.createReadStream(zipFilePath)
.pipe(unzipper.Extract({ path: peripheralsfolder }))
.on('close', () => {
console.log('File unzipped successfully.');
fs.unlinkSync(zipFilePath); // Delete the zip file after extraction
console.log('Zip file deleted.');
console.log('Downloaded blocks');
progressBar.style.display = 'none';
resolve();
})
.on('error', (error) => {
reject(error);
});
});
} catch (error) {
console.error('Error downloading or unzipping file:', error);
}
}
const defineicon = {
computer: {
@ -76,7 +175,7 @@ function loadperipheral(workspace, currenttoolbar, peripherals, usedlibinproject
const newxml = mergeXml(currenttoolbar, toolbar);
workspace.updateToolbox(newxml);
document.getElementById('statusMessage').textContent = `Loaded ${peripherals}`;
fireNotify(`Loaded ${peripherals}`, "success")
// Synchronously require generator.js
require(generatorfilePath); // This will execute generator.js if it's a Node.js module
@ -86,7 +185,7 @@ function loadperipheral(workspace, currenttoolbar, peripherals, usedlibinproject
return newxml;
} catch (e) {
document.getElementById('statusMessage').textContent = `Can't Import ${peripherals}: ${e}`;
fireNotify(`Can't Import ${peripherals}: ${e}`, "error")
ipc.send("error", `Can't Import ${peripherals}: ${e}`);
}
} else {
@ -125,7 +224,6 @@ function addimageiconinfo(div, src, tiptool) {
function scanindex() {
let foundedpackages = 0;
document.getElementById('statusMessage').textContent = "Scanning Packages...";
// clear all item in libcontainer
document.getElementById('libcontainer').innerHTML = "";
@ -173,9 +271,15 @@ function scanindex() {
// Create the title element
const title = document.createElement('h3');
title.textContent = jsonData.name + ` [v${jsonData.version} by ${jsonData.author}]`;
title.textContent = jsonData.name;
libraryDetails.appendChild(title);
// Create the credit and version element
const cv = document.createElement('p');
cv.classList.add('library-details-cv');
cv.textContent = `v${jsonData.version} by ${jsonData.author}`;
libraryDetails.appendChild(cv);
// Create the description element
const description = document.createElement('p');
description.innerHTML = jsonData.description;
@ -230,14 +334,12 @@ function scanindex() {
});
});
document.getElementById('statusMessage').textContent = `Founded ${foundedpackages} Packages`;
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
fireNotify(`Founded ${foundedpackages} Packages`, "success")
}
module.exports = {
loadperipheral,
scanindex
scanindex,
isBlocksFolderEmpty,
downloadBlocks
}

View File

@ -7,109 +7,258 @@ class CCRemote {
host: ip
});
this.clients = new Map(); // Map to track client data (numeric IDs)
this.clientIdCounter = 0; // Counter for numeric client IDs
console.log("Remote server is started");
fireNotify(
"Remote server started, waiting for clients to connect...",
"info"
);
this.socket.on('connection', (ws) => {
document.getElementById("navbar-button-computer-disconnect").disabled = false;
document.getElementById("navbar-button-computer-run").disabled = false;
const clientId = this.clientIdCounter++;
this.clients.set(clientId, { ws, isAlive: true, name: `Client-${clientId}`, clientInfo: null });
document.getElementById('statusMessage').textContent = "Computer connected";
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
if (this.clients.size >= 0) {
document.getElementById("navbar-button-computer-disconnect").disabled = false;
document.getElementById("navbar-button-computer-run").disabled = false;
}
console.log('WebSocket connection established.');
console.log(`Client ${clientId} connected.`);
fireNotify(`Client ${clientId} connected`, "success");
// Send "info" command to the client immediately upon connection
this.sendCommandToClient(clientId, 'info', true);
// Set up heartbeat
ws.isAlive = true;
ws.on('pong', () => {
ws.isAlive = true;
this.clients.get(clientId).isAlive = true;
});
ws.on('message', (message) => {
console.log(`Received message => ${message}`);
console.log(`Message from Client ${clientId}: ${message}`);
// Handle info response
try {
const data = JSON.parse(message);
if (data.OSVersion && typeof data.OSVersion === 'string' &&
data.Name && typeof data.Name === 'string' &&
typeof data.ID === 'number' &&
typeof data.uptime === 'number' &&
typeof data.Type === 'number') {
// Update clientInfo with the received data
this.clients.get(clientId).clientInfo = data;
console.log(`Client ${clientId} info updated:`, data);
} else {
console.log(`Invalid data format from Client ${clientId}:`, data);
}
} catch (error) {
console.error(`Error parsing message from Client ${clientId}:`, error);
}
});
ws.on('close', () => {
document.getElementById("navbar-button-computer-disconnect").disabled = true;
document.getElementById("navbar-button-computer-run").disabled = true;
console.log(`Client ${clientId} disconnected.`);
this.clients.delete(clientId);
fireNotify(`Client ${clientId} disconnected`, "warning");
document.getElementById('statusMessage').textContent = "Computer disconnected";
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
if (this.clients.size === 0) {
document.getElementById("navbar-button-computer-disconnect").disabled = true;
document.getElementById("navbar-button-computer-run").disabled = true;
}
});
ws.on('error', (error) => {
console.error('Client error:', error);
console.error(`Client ${clientId} error:`, error);
fireNotify(`Error with Client ${clientId}: ${error.message}`, "error");
});
});
// Ping clients every 30 seconds to check if they are alive
const interval = setInterval(() => {
this.socket.clients.forEach((ws) => {
if (ws.isAlive === false) {
console.log('Client did not respond to ping, terminating connection.');
return ws.terminate();
for (const [clientId, clientData] of this.clients.entries()) {
if (clientData.isAlive === false) {
console.log(`Client ${clientId} did not respond to ping, terminating connection.`);
fireNotify(`Client ${clientId} did not respond to ping, disconnected`, "warning");
clientData.ws.terminate();
this.clients.delete(clientId);
continue;
}
ws.isAlive = false;
ws.ping();
});
}, 1000);
clientData.isAlive = false;
clientData.ws.ping();
}
}, 30000);
this.socket.on('close', () => {
clearInterval(interval);
console.log('WebSocket server closed.');
fireNotify("WebSocket server closed.", "info");
});
this.socket.on('error', (error) => {
console.error('WebSocket server error:', error);
fireNotify(`WebSocket server error: ${error.message}`, "error");
});
}
isClientConnect() {
for (const client of this.socket.clients) {
if (client.readyState === WebSocket.OPEN) {
return true;
// List all connected clients with their IDs, names, and clientInfo
// List all connected clients with their IDs, names, and clientInfo
async listClients() {
const clientList = [];
// Create an array of promises for fetching client info
const infoPromises = [];
// Loop over each client and trigger 'info' command to get the latest information
for (const [clientId, clientData] of this.clients.entries()) {
if (clientData.ws.readyState === WebSocket.OPEN) {
const infoPromise = new Promise((resolve) => {
// Set a timeout to handle cases where no response is received
const timeout = setTimeout(() => {
resolve({ id: clientId, name: clientData.name, info: null });
}, 5000); // Wait 5 seconds for a response
// Listen for the response from the client
const messageHandler = (message) => {
try {
const data = JSON.parse(message);
if (data.OSVersion && typeof data.OSVersion === 'string' &&
data.Name && typeof data.Name === 'string' &&
typeof data.ID === 'number' &&
typeof data.uptime === 'number' &&
typeof data.Type === 'number') {
// Resolve with the valid data
resolve({ id: clientId, name: clientData.name, info: data });
} else {
resolve({ id: clientId, name: clientData.name, info: null });
}
} catch (error) {
resolve({ id: clientId, name: clientData.name, info: null });
}
};
// Send the "info" command to the client
this.sendCommandToClient(clientId, 'info', true);
// Attach the handler for this specific client
clientData.ws.on('message', messageHandler);
});
// Add the promise to the array
infoPromises.push(infoPromise);
}
}
return false;
// Wait for all promises to resolve (all client info to be updated)
const resolvedClientInfo = await Promise.all(infoPromises);
// Construct the final list of clients with updated information
for (const clientInfo of resolvedClientInfo) {
clientList.push(clientInfo);
}
return clientList;
}
sendCommand(command) {
this.socket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(command);
}
});
// Check if any client is connected
isClientConnect() {
return this.clients.size > 0;
}
sendCode(Code) {
this.socket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send("sendcode");
// Send code to all clients
sendCode(code) {
this.clients.forEach((clientData, clientId) => {
if (clientData.ws.readyState === WebSocket.OPEN) {
clientData.ws.send("sendcode");
setTimeout(() => {
client.send(Code);
clientData.ws.send(code);
}, 500);
}
});
}
// Send code to a specific client
sendCodeToClient(clientId, code) {
const client = this.clients.get(clientId);
if (!client || client.ws.readyState !== WebSocket.OPEN) {
console.error(`Client ${clientId} is not available or not connected.`);
fireNotify(`Client ${clientId} is not connected.`, "error");
return;
}
client.ws.send("sendcode");
setTimeout(() => {
client.ws.send(code);
}, 500);
}
// Run code on all clients
runCode() {
this.socket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send("runcode");
this.clients.forEach((clientData, clientId) => {
if (clientData.ws.readyState === WebSocket.OPEN) {
clientData.ws.send("runcode");
}
});
}
// Run code on a specific client
runCodeOnClient(clientId) {
const client = this.clients.get(clientId);
if (!client || client.ws.readyState !== WebSocket.OPEN) {
console.error(`Client ${clientId} is not available or not connected.`);
fireNotify(`Client ${clientId} is not connected.`, "error");
return;
}
client.ws.send("runcode");
}
// Disconnect all clients
disconnectAllClients() {
this.socket.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.close();
this.clients.forEach((clientData, clientId) => {
if (clientData.ws.readyState === WebSocket.OPEN) {
clientData.ws.send("exit");
clientData.ws.close();
fireNotify(`Client ${clientId} disconnected`, "warning");
}
});
this.clients.clear();
}
// Send a command to a specific client
sendCommandToClient(clientId, command, expectResponse = false) {
const client = this.clients.get(clientId);
if (!client || client.ws.readyState !== WebSocket.OPEN) {
console.error(`Client ${clientId} is not available or not connected.`);
fireNotify(`Client ${clientId} is not connected.`, "error");
return;
}
client.ws.send(command);
if (expectResponse) {
client.ws.on('message', (response) => {
console.log(`Response from Client ${clientId}: ${response}`);
});
}
}
// Send a command to all clients
sendCommandToAll(command, expectResponse = false) {
for (const [clientId, clientData] of this.clients.entries()) {
if (clientData.ws.readyState === WebSocket.OPEN) {
clientData.ws.send(command);
if (expectResponse) {
clientData.ws.on('message', (response) => {
console.log(`Response from Client ${clientId}: ${response}`);
});
}
}
}
}
}

View File

@ -9,6 +9,16 @@ const circles = document.querySelectorAll(".circle");
let upcurrentActive = 1;
let uploadError = false; // Flag to track if there's an error
const defineicon = {
5: path.join(__dirname, '..', 'assets', 'basic_computer.png'),
6: path.join(__dirname, '..', 'assets', 'adv_computer.png'),
7: path.join(__dirname, '..', 'assets', 'command_computer.png'),
2: path.join(__dirname, '..', 'assets', 'pocket_computer.png'),
4: path.join(__dirname, '..', 'assets', 'adv_pocket_computer.png'),
1: path.join(__dirname, '..', 'assets', 'turtle.png'),
3: path.join(__dirname, '..', 'assets', 'adv_turtle.png')
}
const uploadUpdateProgress = () => {
circles.forEach((circle, index) => {
if (index < upcurrentActive) {
@ -34,7 +44,6 @@ const uploadUpdateProgress = () => {
};
function clientexit() {
ccInstance.sendCommand("exit")
ccInstance.disconnectAllClients();
}
@ -42,15 +51,17 @@ function gencodeonly() {
return luaGenerator.workspaceToCode(workspace);
}
let selectedClientId = null;
async function gencode() {
console.log("Starting generate code")
document.getElementById('upload-popup').style.display = 'block';
upcurrentActive = 1;
uploadError = false;
uploadUpdateProgress();
// compile/convert code
console.log("Generating code...")
// Compile/convert code
console.log("Generating code...");
upcurrentActive++;
uploadUpdateProgress();
document.getElementById('upload-status').textContent = "Generating code";
@ -61,37 +72,108 @@ async function gencode() {
uploadError = true;
uploadUpdateProgress();
document.getElementById('upload-status').textContent = e;
return
return;
}
upcurrentActive++;
uploadUpdateProgress();
if (ccInstance.isClientConnect()) {
// upload to computercraft with remote
console.log("Uploading code to machine...")
document.getElementById('upload-status').textContent = "Uploading code to machine";
ccInstance.sendCode(code);
await delay(500)
const clients = await ccInstance.listClients();
document.getElementById('upload-status').innerHTML = "";
// execute with remote
console.log("Executing code in machine...")
document.getElementById('upload-status').textContent = "Executing code";
upcurrentActive++;
uploadUpdateProgress();
ccInstance.runCode();
const ClientLists = document.createElement('div');
ClientLists.classList.add("library-content");
clients.forEach(client => {
console.log(`Info:`, client.info);
const ClientItem = document.createElement('div');
ClientItem.classList.add('library-item', 'overflow-auto', 'library-container');
ClientItem.setAttribute('data-clientid', client.id);
const img = document.createElement('img');
img.classList.add('libimage');
img.src = defineicon[client.info.Type];
ClientItem.appendChild(img);
const ClientDetails = document.createElement('div');
ClientDetails.classList.add('library-details');
const title = document.createElement('h3');
title.textContent = client.info.Name;
ClientDetails.appendChild(title);
const description = document.createElement('p');
description.innerHTML = `OS: ${client.info.OSVersion} | ID: ${client.info.ID} | Uptime: ${client.info.uptime}s`;
ClientDetails.appendChild(description);
ClientItem.appendChild(ClientDetails);
// Add event listener for user selection
ClientItem.addEventListener('click', () => {
selectedClientId = client.id; // Correctly store the client ID
ClientItem.classList.add('selected'); // Optional: Style selected item
});
ClientLists.appendChild(ClientItem);
});
const title = document.createElement("h3");
title.textContent = "Upload to machine";
document.getElementById('upload-status').appendChild(title);
document.getElementById('upload-status').appendChild(ClientLists);
// Wait for user selection
const waitForSelection = () => new Promise((resolve, reject) => {
const checkSelection = setInterval(() => {
if (selectedClientId !== null) {
clearInterval(checkSelection);
resolve(selectedClientId);
}
}, 100); // Check every 100ms if a client is selected
});
// Wait until the user selects a client
try {
const selectedClientId = await waitForSelection();
console.log(`Selected client: ${selectedClientId}`);
document.getElementById('upload-status').innerHTML = "";
// Now proceed with uploading code to the selected client
console.log("Uploading code to machine...");
document.getElementById('upload-status').textContent = "Uploading code to machine";
ccInstance.sendCodeToClient(selectedClientId, code);
await delay(500);
// Execute with remote
console.log("Executing code in machine...");
document.getElementById('upload-status').textContent = "Executing code";
upcurrentActive++;
uploadUpdateProgress();
ccInstance.runCodeOnClient(selectedClientId);
} catch (error) {
console.error("Error waiting for selection:", error);
uploadError = true;
uploadUpdateProgress();
document.getElementById('upload-status').innerHTML = `Error selecting client.`;
}
} else {
console.log("Machine is not connected")
console.log("Machine is not connected");
uploadError = true;
uploadUpdateProgress();
document.getElementById('upload-status').innerHTML = `Please Connect Computer to IDE.\nInstruction: <a href="https://github.com/DPSoftware-Foundation/ccIDE#install-remote-code-into-computercraft">Install Remote code into computercraft</a> in github. (Please press SHIFT or CTRL and click)`;
return
document.getElementById('upload-status').innerHTML = `Please Connect Computer to IDE.\nInstruction: <a href="https://github.com/DPSoftware-Foundation/ccIDE#install-remote-code-into-computercraft">Install Remote code into computercraft</a> in github.`;
return;
}
// done!
console.log("Run code done!")
// Done!
console.log("Run code done!");
document.getElementById('upload-status').textContent = "Done!";
await delay(1000)
await delay(1000);
document.getElementById('upload-popup').style.animation = 'fadeOut 0.3s ease'; // Apply fade-out animation
selectedClientId = null;
setTimeout(function() {
document.getElementById('upload-popup').style.display = 'none'; // Hide popup after animation completes
document.getElementById('upload-popup').style.animation = ''; // Reset animation property

View File

@ -3,137 +3,152 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;700&display=swap" rel="stylesheet">
<script>
const {shell} = require('electron');
</script>
<link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
<script src="../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>
<!-- Navigation bar -->
<nav id="navbar">
<!--
<button class="navbar-button" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Computer Types" style="background-color: #0087bd;">
<svg viewBox="0 0 24 24"><path d="M6 4h12v1h3v2h-3v2h3v2h-3v2h3v2h-3v2h3v2h-3v1H6v-1H3v-2h3v-2H3v-2h3v-2H3V9h3V7H3V5h3V4m5 11v3h1v-3h-1m2 0v3h1v-3h-1m2 0v3h1v-3h-1z" /></svg>
</button>
-->
<button class="navbar-button" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Packages Managers" style="background-color: #0087bd;" onclick="openlibraryselect()">
<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" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Preview code" style="background-color: #0087bd; margin-left: auto; position: absolute; right: 115px;">
<svg viewBox="0 0 24 24"><path d="M12 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0 8a5 5 0 0 1-5-5 5 5 0 0 1 5-5 5 5 0 0 1 5 5 5 5 0 0 1-5 5m0-12.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5z" /></svg>
</button>
-->
<button class="navbar-button" id="navbar-button-computer-disconnect" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Disconnect Client" style="background-color: #bd0000; margin-left: auto; position: absolute; right: 65px;" onclick="clientexit()" disabled>
<svg viewBox="0 0 24 24"><path d="M19 3H5c-1.11 0-2 .89-2 2v4h2V5h14v14H5v-4H3v4a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-8.92 12.58L11.5 17l5-5-5-5-1.42 1.41L12.67 11H3v2h9.67l-2.59 2.58z" /></svg>
</button>
<button class="navbar-button" id="navbar-button-computer-run" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Run" style="background-color: #10bd00; margin-left: auto;" onclick="gencode()" disabled>
<svg viewBox="0 0 24 24"><path d="M8 5.14v14l11-7-11-7z" /></svg>
</button>
</nav>
<!-- <meta charset="utf-8" http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-m0mwdxEgv9nMDDw3gHrhX/xCs/OAiZg5/bQ/6X5SIcA='"/> -->
<div class="loading-area">
<div id="loadingContent">
<div class="loader"></div>
<br>
<h3>Please wait...</h3>
<h5 id="loadingstatus"></h5>
<progress id="progressBarloading" value="0" max="100"></progress>
<!-- Blockly workspace container -->
<section id="blocklyContainer">
<div id="blocklyDiv"></div>
</section>
<!-- Status bar -->
<div id="statusBar">
<p id="statusMessage">Initializing...</p>
</div>
<div class="popup" id="upload-popup">
<div class="popup-content">
<button type="button" class="btn-close float-end" aria-label="Close" id="uploadCloseBtn"></button>
<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>
<div class="popup" id="about-popup">
<div class="popup-content">
<button type="button" class="btn-close float-end" aria-label="Close" id="aboutCloseBtn"></button>
<h2>ccIDE</h2>
<p>
ccIDE is an integrated development environment designed for coding Lua scripts
for ComputerCraft. It provides a user-friendly interface for creating and managing
projects, designing custom blocks, and debugging scripts efficiently.
</p>
<p>
Developed by DPSoftware Foundation, ccIDE aims to streamline the development
process for ComputerCraft programmers, offering features like block-based coding,
real-time collaboration, and seamless integration with ComputerCraft environments.
</p>
<p>
Visit <a href="https://damp11113.xyz/dpsoftware/ccide">ccIDE Website</a> for more information and updates.
</p>
<div class="license">
&copy; 2024 DPSoftware Foundation. Licensed under GPL v3.
</div>
<style>
.license {
font-size: 14px;
color: #888;
text-align: center;
margin-top: 20px;
}
</style>
</div>
</div>
<div class="popup" id="library-popup" style="overflow-y:hidden;">
<div class="popup-content p-3" style="max-width: 1280px; position: relative; margin-top: 100px;">
<button type="button" class="btn-close" aria-label="Close" id="libraryCloseBtn" style="position: absolute; top: 10px; right: 10px;"></button>
<h3>Packages Managers</h3>
<div class="library-content">
<div id="libcontainer">
<div class="library-item overflow-auto library-container" data-libraryfolder="name">
<img src="image.jpg" class="libimage" onerror="this.onerror=null;this.src='../assets/noimagefallback.png'; this.alt='No Image Available';">
<div class="library-details">
<h3>Title [v1.0 by Author]</h3>
<p>Library description goes here.</p>
<img src="../assets/basic_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Basic Computer">
<img src="../assets/adv_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Advanced Computer">
<img src="../assets/command_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Command Computer">
<img src="../assets/pocket_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Pocket Computer">
<img src="../assets/adv_pocket_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Advanced Pocket Computer">
<img src="../assets/turtle.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Basic Turtle">
<img src="../assets/adv_turtle.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Advanced Turtle">
<img src="../assets/peripheral.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Peripheral">
<img src="../assets/library.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Library">
<img src="../assets/network-require.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Network Require">
</div>
</div>
</div>
</div>
<div class="button-container">
<button type="button" class="btn btn-success btn-sm" id="packageman-import-btn">Import Packages</button>
<button type="button" class="btn btn-warning btn-sm" onclick='scanindex()'>Refetch Packages</button>
<button type="button" class="btn btn-primary btn-sm" onclick='shell.openExternal("https://damp11113.xyz/dpsoftware/ccide/library")'>Download Packages</button>
<div class="dpclogoload">
<img src="../assets/DPSoftware2.png" style="width: 25%;"/>
</div>
</div>
</div>
<link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="../node_modules/toastify-js/src/toastify.css">
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body>
<div class="content-area">
<!-- Navigation bar -->
<nav id="navbar">
<!--
<button class="navbar-button" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Computer Types" style="background-color: #0087bd;">
<svg viewBox="0 0 24 24"><path d="M6 4h12v1h3v2h-3v2h3v2h-3v2h3v2h-3v2h3v2h-3v1H6v-1H3v-2h3v-2H3v-2h3v-2H3V9h3V7H3V5h3V4m5 11v3h1v-3h-1m2 0v3h1v-3h-1m2 0v3h1v-3h-1z" /></svg>
</button>
-->
<button class="navbar-button" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Packages Managers" style="background-color: #0087bd;" onclick="openlibraryselect()">
<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" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Preview code" style="background-color: #0087bd; margin-left: auto; position: absolute; right: 115px;">
<svg viewBox="0 0 24 24"><path d="M12 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0 8a5 5 0 0 1-5-5 5 5 0 0 1 5-5 5 5 0 0 1 5 5 5 5 0 0 1-5 5m0-12.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5z" /></svg>
</button>
-->
<button class="navbar-button" id="navbar-button-computer-disconnect" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Disconnect Client" style="background-color: #bd0000; margin-left: auto; position: absolute; right: 65px;" onclick="clientexit()" disabled>
<svg viewBox="0 0 24 24"><path d="M19 3H5c-1.11 0-2 .89-2 2v4h2V5h14v14H5v-4H3v4a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-8.92 12.58L11.5 17l5-5-5-5-1.42 1.41L12.67 11H3v2h9.67l-2.59 2.58z" /></svg>
</button>
<button class="navbar-button" id="navbar-button-computer-run" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Run" style="background-color: #10bd00; margin-left: auto;" onclick="gencode()" disabled>
<svg viewBox="0 0 24 24"><path d="M8 5.14v14l11-7-11-7z" /></svg>
</button>
</nav>
<!-- Blockly workspace container -->
<section id="blocklyContainer">
<div id="blocklyDiv"></div>
</section>
<div class="popup" id="upload-popup" style="overflow-y:hidden;">
<div class="popup-content p-3" style="max-width: 720px; position: relative; margin-top: 100px;">
<button type="button" class="btn-close float-end" aria-label="Close" id="uploadCloseBtn"></button>
<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>
<div id="upload-status"></div>
</div>
</div>
</div>
<div class="popup" id="about-popup">
<div class="popup-content">
<button type="button" class="btn-close float-end" aria-label="Close" id="aboutCloseBtn"></button>
<h2>ccIDE</h2>
<p>
ccIDE is an integrated development environment designed for coding Lua scripts
for ComputerCraft. It provides a user-friendly interface for creating and managing
projects, designing custom blocks, and debugging scripts efficiently.
</p>
<p>
Developed by DPSoftware Foundation, ccIDE aims to streamline the development
process for ComputerCraft programmers, offering features like block-based coding,
real-time collaboration, and seamless integration with ComputerCraft environments.
</p>
<p>
Visit <a href="https://damp11113.xyz/dpsoftware/ccide">ccIDE Website</a> for more information and updates.
</p>
<div class="license">
&copy; 2024 DPSoftware Foundation. Licensed under GPL v3.
</div>
<style>
.license {
font-size: 14px;
color: #888;
text-align: center;
margin-top: 20px;
}
</style>
</div>
</div>
<div class="popup" id="library-popup" style="overflow-y:hidden;">
<div class="popup-content p-3" style="max-width: 1280px; position: relative; margin-top: 100px;">
<button type="button" class="btn-close" aria-label="Close" id="libraryCloseBtn" style="position: absolute; top: 10px; right: 10px;"></button>
<h3>Packages Managers</h3>
<div class="library-content">
<div id="libcontainer">
<div class="library-item overflow-auto library-container" data-libraryfolder="name">
<img src="image.jpg" class="libimage" onerror="this.onerror=null;this.src='../assets/noimagefallback.png'; this.alt='No Image Available';">
<div class="library-details">
<h3>Title</h3>
<!--Add Author credit-->
<h6>v1.0 by Author</h6>
<p>Library description goes here.</p>
<img src="../assets/basic_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Basic Computer">
<img src="../assets/adv_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Advanced Computer">
<img src="../assets/command_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Command Computer">
<img src="../assets/pocket_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Pocket Computer">
<img src="../assets/adv_pocket_computer.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Advanced Pocket Computer">
<img src="../assets/turtle.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Basic Turtle">
<img src="../assets/adv_turtle.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Advanced Turtle">
<img src="../assets/peripheral.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Peripheral">
<img src="../assets/library.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Library">
<img src="../assets/network-require.png" class="libimageicon" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Network Require">
</div>
</div>
</div>
</div>
<div class="button-container">
<button type="button" class="btn btn-success btn-sm" id="packageman-import-btn">Import Packages</button>
<button type="button" class="btn btn-warning btn-sm" onclick='scanindex()'>Refetch Packages</button>
<button type="button" class="btn btn-primary btn-sm" onclick='downloadBlocks()'>Download Packages</button>
</div>
</div>
</div>
</div>
<script>
const {shell} = require('electron');
</script>
<script src="../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="frontend.js"></script>
<script src="index.js"></script>
<script src="codegen.js"></script>

View File

@ -20,14 +20,43 @@ console.originalLog = console.log;
console.log = function (...args) {
ipc.send("update-log-status", ...args)
console.originalLog(...args)
document.getElementById("loadingstatus").textContent = args.join(' ');
};
console.log("Importing module...")
const fs = require('fs');
const path = require('path');
const { loadperipheral, scanindex } = require("./blocksmanager");
const { loadperipheral, scanindex, isBlocksFolderEmpty, downloadBlocks } = require("./blocksmanager");
const Blockly = require('blockly');
const { WorkspaceSearch } = require("@blockly/plugin-workspace-search")
const Toastify = require('toastify-js');
function fireNotify(text, status, destination, duration=3000) {
let color;
if (status == "success") {
color = "#a5dc86"
} else if (status == "warning") {
color = "#f8bb86"
} else if (status == "error") {
color = "#f27474"
} else if (status == "info") {
color = "#3fc3ee"
}
Toastify({
text: text,
duration: duration,
destination: destination,
newWindow: true,
close: true,
gravity: "top", // `top` or `bottom`
position: "right", // `left`, `center` or `right`
stopOnFocus: true, // Prevents dismissing of toast on hover
style: {
background: color,
}
}).showToast();
}
let isprojectsaved = false;
let isprojectopened = false;
@ -39,6 +68,24 @@ Blockly.utils.colour.setHsvSaturation(0.9)
let originaltoolbar = fs.readFileSync(path.join(__dirname, "toolbox.xml"), 'utf8');
try {
const theme = Blockly.Theme.defineTheme('themeName', {
base: Blockly.Themes.Classic,
componentStyles: {
workspaceBackgroundColour: "#1e1e1e",
toolboxBackgroundColour: "blackBackground",
toolboxForegroundColour: "#fff",
flyoutBackgroundColour: "#252526",
flyoutForegroundColour: "#ccc",
flyoutOpacity: 1,
scrollbarColour: "#797979",
insertionMarkerColour: "#fff",
insertionMarkerOpacity: .3,
scrollbarOpacity: .4,
cursorColour: "#d0d0d0",
blackBackground: "#333"
}
});
var workspace = Blockly.inject('blocklyDiv', {
toolbox: originaltoolbar,
trashcan: true,
@ -56,7 +103,8 @@ try {
minScale: 0.1,
scaleSpeed: 1.1,
pinch: true
}
},
theme: theme
});
const workspaceSearch = new WorkspaceSearch(workspace);
@ -67,27 +115,6 @@ try {
ipc.send("erroronstart", `Error on initializing workspace: ${e}`)
}
try {
console.log("Importing system library...")
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]);
}
};
}
}
require("./module_generator")
console.log("Scanning library...")
scanindex();
} catch (e) {
ipc.send("erroronstart", `Error on loading block: ${e}`)
}
console.log("Initializing event...")
ipc.on('export-lua-request', (event) => {
@ -98,7 +125,7 @@ ipc.on('export-lua-request', (event) => {
// Save workspace
ipc.on('save-workspace-request', (event) => {
console.log("Saving project...")
document.getElementById('statusMessage').textContent = `Saving...`;
fireNotify(`Saving...`, "warning")
const state = Blockly.serialization.workspaces.save(workspace);
const data = {
"usedlibrary": usedlibinproject,
@ -120,28 +147,21 @@ ipc.on('load-workspace', (event, json) => {
const packagefolder = libinproject[i]
if (!usedlibinproject.includes(packagefolder)) {
try {
originaltoolbar = loadperipheral(workspace, originaltoolbar, packagefolder);
originaltoolbar = loadperipheral(workspace, originaltoolbar, packagefolder, usedlibinproject);
usedlibinproject.push(packagefolder);
} catch (e) {
document.getElementById('statusMessage').textContent = `Can't Import ${usedlibinproject[i]}: ${e}`;
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
fireNotify(`Can't Import ${usedlibinproject[i]}: ${e}`, "error")
}
}
}
setTimeout(() => {
Blockly.serialization.workspaces.load(data.content, workspace);
isprojectsaved = true
document.getElementById('statusMessage').textContent = `Project Loaded`;
}, 100);
Blockly.serialization.workspaces.load(data.content, workspace);
isprojectsaved = true
fireNotify(`Project Loaded`, "success")
}
} catch (e) {
document.getElementById('statusMessage').textContent = `Can't Load Project: ${e}`;
fireNotify(`Can't Load Project: ${e}`, "error")
}
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
})
ipc.on('workspace-saved', (event, success) => {
@ -149,10 +169,7 @@ ipc.on('workspace-saved', (event, success) => {
if (!isprojectopened) {
isprojectopened = true;
}
document.getElementById('statusMessage').textContent = `Project Saved`;
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
fireNotify(`Project Saved`, "success")
});
ipc.on("open-about", () => {
@ -186,19 +203,53 @@ document.getElementById("packageman-import-btn").addEventListener('click', () =>
selectedItems.forEach(item => {
const packagefolder = item.getAttribute('data-libraryfolder');
originaltoolbar = loadperipheral(workspace, originaltoolbar, packagefolder, usedlibinproject);
fireNotify(`Loaded ${packagefolder}`, "success")
});
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 1000);
});
// Ensure Blockly container is shown after the workspace is injected
console.log("Finished")
setTimeout(() => {
ipc.send("ready")
}, 500);
async function InitializingBlock() {
try {
console.log("Importing system library...");
const sysmodulejson = fs.readFileSync(path.join(__dirname, "module_block_design.json"), 'utf8');
const blocksJson = JSON.parse(sysmodulejson);
document.getElementById('statusMessage').textContent = "Computer isn't connect";
setTimeout(() => {
document.getElementById('statusMessage').textContent = `Ready`;
}, 10000);
for (const blockId in blocksJson) {
if (blocksJson.hasOwnProperty(blockId)) {
Blockly.Blocks[blockId] = {
init: function() {
this.jsonInit(blocksJson[blockId]);
}
};
}
}
require("./module_generator");
console.log("Check library folder...");
if (isBlocksFolderEmpty()) {
await downloadBlocks();
}
scanindex();
} catch (e) {
ipc.send("erroronstart", `Error on loading block: ${e}`);
}
// Ensure Blockly container is shown after the workspace is injected
console.log("Finished")
setTimeout(() => {
ipc.send("ready")
// Hide loading area
const loadingArea = document.querySelector('.loading-area');
loadingArea.style.opacity = '0';
loadingArea.style.visibility = 'hidden';
loadingArea.style.display = 'none';
// Show content area
const contentArea = document.querySelector('.content-area');
contentArea.style.opacity = '1';
contentArea.style.visibility = 'visible';
}, 500);
}
InitializingBlock();

View File

@ -1,16 +1,90 @@
:root {
--line-border-fill: #3498db;
--line-border-empty: #e0e0e0;
--line-border-empty: #7f8c8d; /* Lighter gray for dark mode */
--background-color: #121212; /* Dark background for body */
--text-color: #e0e0e0; /* Lighter text color */
--button-bg: #333; /* Dark button background */
--button-hover-bg: #0066cc; /* Button hover color */
--progress-bg: #3498db; /* Progress bar fill color */
--error-color: #e74c3c; /* Error state color */
}
body {
background-color: #212121;
margin: 0;
background-color: var(--background-color);
color: var(--text-color);
font-family: 'Arial', sans-serif;
overflow: hidden; /* Hide scrollbars */
}
.content-area {
opacity: 0;
visibility: hidden;
}
.loading-area {
color: #cacaca;
position: absolute;
z-index: 400;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: calc(100% - 22px);
}
#progressBarloading {
display: none; /* Initially hidden */
}
#loadingContent {
position: relative;
display: flex;
flex-direction: column;
height: 100vh;
font-family: 'Noto Sans', sans-serif;
overflow: hidden; /* Hide scrollbars */
align-items: center;
justify-content: center;
}
.loader {
width: 90px;
height: 14px;
box-shadow: 0 3px 0 #fff;
position: relative;
clip-path: inset(-40px 0 -5px)
}
.loader:before {
content: "";
position: absolute;
inset: auto calc(50% - 17px) 0;
height: 50px;
--g:no-repeat linear-gradient(#ccc 0 0);
background: var(--g),var(--g),var(--g),var(--g);
background-size: 16px 14px;
animation:
l7-1 2s infinite linear,
l7-2 2s infinite linear;
}
@keyframes l7-1 {
0%,
100% {background-position: 0 -50px,100% -50px}
17.5% {background-position: 0 100%,100% -50px,0 -50px,100% -50px}
35% {background-position: 0 100%,100% 100% ,0 -50px,100% -50px}
52.5% {background-position: 0 100%,100% 100% ,0 calc(100% - 16px),100% -50px}
70%,
98% {background-position: 0 100%,100% 100% ,0 calc(100% - 16px),100% calc(100% - 16px)}
}
@keyframes l7-2 {
0%,70% {transform:translate(0)}
100% {transform:translate(200%)}
}
.dpclogoload {
display: flex; /* Enable flexbox */
justify-content: center; /* Center horizontally */
position: fixed; /* Fixed position */
bottom: 30px; /* Adjust as necessary */
z-index: 20; /* Ensure it appears above other content */
width: 100%; /* Ensure the div takes full width */
}
#navbar {
@ -22,7 +96,7 @@ body {
}
.navbar-button {
background-color: #2c3e50;
background-color: var(--button-bg);
color: white;
border: none;
border-radius: 50%; /* Make the button circular */
@ -48,7 +122,7 @@ body {
}
.navbar-button:hover {
background-color: #0066cc; /* Darken color on hover */
background-color: var(--button-hover-bg); /* Darken color on hover */
z-index: 99;
}
@ -66,20 +140,6 @@ body {
bottom: 0;
}
#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;
}
/* Styles for the upload-popup container */
.popup {
display: none; /* Hide initially */
@ -97,7 +157,8 @@ body {
/* Styles for the upload-popup content */
.popup-content {
background-color: #fefefe;
background-color: #333;
color: #fefefe;
margin: 15% auto; /* Center the upload-popup vertically and horizontally */
padding: 20px;
border: 1px solid #888;
@ -151,7 +212,7 @@ body {
}
.progress {
background-color: var(--line-border-fill);
background-color: var(--progress-bg);
position: absolute;
top: 50%;
left: 0;
@ -163,10 +224,9 @@ body {
}
.progress.error {
background-color: red; /* Set to red color for error state */
background-color: var(--error-color); /* Set to red color for error state */
}
.circle {
background-color: #fff;
color: #999;
@ -186,7 +246,7 @@ body {
}
.circle.error {
border-color: red; /* Optionally, change border color to red */
border-color: var(--error-color); /* Optionally, change border color to red */
color: white; /* Optionally, adjust text color for visibility */
}
@ -211,15 +271,20 @@ body {
}
.library-details {
flex: 1;
line-height: 10px;
line-height: 5px;
}
.library-details-cv {
color: #7a7a7a;
font-size: 15px;
}
.library-item {
cursor: pointer;
padding: 10px;
border-bottom: 1px solid #e9ecef;
border-bottom: 1px solid #7f8c8d; /* Lighter border */
}
.library-item.selected {
background-color: #e9ecef;
background-color: #34495e; /* Darker background for selected items */
}
.library-content {
flex: 1;
@ -228,11 +293,27 @@ body {
max-height: 720px; /* Ensure a max height for scrolling */
}
.library-item:hover {
background-color: #cacaca;
background-color: #7f8c8d; /* Hover effect for items */
}
.button-container {
display: flex;
justify-content: flex-end;
gap: 10px;
}
justify-content: center;
align-items: center;
margin-top: 10px;
}
.btn-invisible {
background: transparent;
border: none;
padding: 5px;
cursor: pointer;
}
.swipe-text {
font-size: 14px;
color: #ccc;
}
.pointer-pointer {
cursor: pointer;
}

117
startup.lua Normal file
View File

@ -0,0 +1,117 @@
print("checking computer type...")
local machineType = 0
local isAdvanced = false
local isPocket = false
local isTurtle = false
local isCommand = false
-- Check if advanced (multishell exists in advanced computers and advanced pocket computers)
if multishell then
isAdvanced = true
end
-- Check if Pocket Computer (API specific to Pocket Computers)
if pocket then
isPocket = true
end
-- Check if Turtle (turtle API is available for turtles)
if turtle then
isTurtle = true
end
if commands then
isCommand = true
end
-- Determine machine type
if isTurtle then
if isAdvanced then
machineType = 3 -- Advanced Turtle
else
machineType = 1 -- Standard Turtle
end
elseif isPocket then
if isAdvanced then
machineType = 4 -- Advanced Pocket Computer
else
machineType = 2 -- Standard Pocket Computer
end
else
if isAdvanced then
if isCommand then
machineType = 7 -- Command Computer
else
machineType = 6 -- Advanced Computer
end
else
machineType = 5 -- Standard Computer
end
end
print("Machine Type: " .. machineType)
print("connecting to IDE...")
local ws = assert(http.websocket("ws://127.0.0.1:5133"))
print("connected to server")
local id
local isrunning = true
function exitcheck()
while true do
local event = os.pullEventRaw("terminate")
if event == "terminate" then
print("Exiting...")
isrunning = false
ws.close()
break
end
end
end
function main()
while isrunning do
print("ready")
local message, error = ws.receive()
if message then
print("Received message:", message)
if message == "ping" then
ws.send("pong")
elseif message == "info" then
info = {
OSVersion = os.version(),
ID = os.getComputerID(),
Name = os.getComputerLabel() or "No Name",
Type = machineType,
uptime = os.clock()
}
ws.send(textutils.serialiseJSON(info))
elseif message == "sendcode" then
local file = io.open("main.lua", "w")
print("waiting for code")
local filedata, error = ws.receive()
file:write(filedata)
file:close()
elseif message == "runcode" then
if isAdvanced then
id = multishell.launch({}, "main.lua")
multishell.setTitle(id, "Code")
multishell.setFocus(id)
else
shell.run("main")
end
elseif message == "exit" then
print("Exiting...")
break
end
else
print("WebSocket error:", error)
break
end
end
end
parallel.waitForAny(exitcheck, main)
print("Exited")

23
webpack.main.config.js Normal file
View File

@ -0,0 +1,23 @@
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
module.exports = {
mode: "production",
target: "electron-main", // For Electron main process
entry: "./src/index.js", // Adjust based on your main entry file
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
externals: [nodeExternals()], // Exclude node_modules
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: "assets", to: "assets" },
{ from: "blocks", to: "blocks" },
{ from: "src/styles.css", to: "styles.css" },
],
}),
],
};

View File

@ -0,0 +1,18 @@
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "production",
target: "web", // This should target the web environment for the renderer process
entry: "./src/frontend.js", // Ensure this points to the correct renderer entry
output: {
filename: "renderer.js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new HtmlWebpackPlugin({
template: "src/index.html", // Ensure this injects the renderer script
}),
],
};