@protorobotics/jenga 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/jenga.js +81 -77
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.0 --- 2026-02-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added shadow blocks
|
|
8
|
+
- Support for newlines using `\n`
|
|
9
|
+
- Toggle for inline inputs
|
|
10
|
+
- Help button
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Additional input types and removed one-input limit
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
|
|
18
|
+
- Removed default blocks
|
|
19
|
+
|
|
3
20
|
## 1.1.0 --- 2025-10-08
|
|
4
21
|
|
|
5
22
|
### Changed
|
package/jenga.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Block, CodeGenerator, common, Field, Toolbox } from "blockly";
|
|
1
|
+
import { Block, CodeGenerator, common, Field, Toolbox, FieldImage, utils } from "blockly";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef {object} Category
|
|
@@ -12,8 +12,15 @@ import { Block, CodeGenerator, common, Field, Toolbox } from "blockly";
|
|
|
12
12
|
* @property {string} name The name of the entry.
|
|
13
13
|
* @property {string} description Description of the entry.
|
|
14
14
|
* @property {BlocklyField[]} blocklyTemplate The template used to make the blockly block.
|
|
15
|
-
* @property {BlocklyIOConnection?} blocklyInput The input connection for the block.
|
|
16
15
|
* @property {BlocklyIOConnection?} blocklyOutput The output connection for the block.
|
|
16
|
+
* @property {boolean} inputsInline Whether the inputs are inline. Optional parameter only used in backend.
|
|
17
|
+
* @property {function(?): object} save Function that saves extra state for the block. Optional parameter only used in backend.
|
|
18
|
+
* @property {function(object): void} load Function that loads extra state for the block. Optional parameter only used in backend.
|
|
19
|
+
* @property {function(): void} update Function that updates the block shape based on state. Optional parameter only used in backend.
|
|
20
|
+
* @property {function(object): void} saveConnections Function that saves connections for the block. Optional parameter only used in backend.
|
|
21
|
+
* @property {function(object): object} compose Function that composes the block from saved connections. Optional parameter only used in backend.
|
|
22
|
+
* @property {function(object): object} decompose Function that decomposes the block to save connections. Optional parameter only used in backend.
|
|
23
|
+
* @property {function(object): void} onchange Function that handles block changes. Optional parameter
|
|
17
24
|
* @property {CodeGenFunction} codeGenerator The code generator function used by blockly.
|
|
18
25
|
*/
|
|
19
26
|
|
|
@@ -27,12 +34,14 @@ import { Block, CodeGenerator, common, Field, Toolbox } from "blockly";
|
|
|
27
34
|
* @property {string?} name The name of the field.
|
|
28
35
|
* @property {FieldFunction?} field Function that generates the blockly field object.
|
|
29
36
|
* @property {string?} text Text displayed in place of the field.
|
|
37
|
+
* @property {BlocklyIOConnection?} blocklyInput The input connection displayed in place of the field.
|
|
30
38
|
*/
|
|
31
39
|
|
|
32
40
|
/**
|
|
33
41
|
* @typedef {object} BlocklyIOConnection
|
|
34
42
|
* @property {BlocklyType} type The type of the connection.
|
|
35
43
|
* @property {string} name The name of the connection.
|
|
44
|
+
* @property {string?} shadow The name of the optional shadow block to use for this connection. Only used for block inputs.
|
|
36
45
|
*/
|
|
37
46
|
|
|
38
47
|
// TODO: define vocab objects
|
|
@@ -42,7 +51,12 @@ import { Block, CodeGenerator, common, Field, Toolbox } from "blockly";
|
|
|
42
51
|
*/
|
|
43
52
|
const BlocklyType = {
|
|
44
53
|
bool: "Boolean",
|
|
45
|
-
|
|
54
|
+
number: "Number",
|
|
55
|
+
string: "String",
|
|
56
|
+
any: "Any",
|
|
57
|
+
void: "Void",
|
|
58
|
+
array: "Array",
|
|
59
|
+
color: "Colour",
|
|
46
60
|
};
|
|
47
61
|
|
|
48
62
|
/**
|
|
@@ -100,23 +114,69 @@ function initBlocklyCategory(category) {
|
|
|
100
114
|
function initBlocklyBlock(entry, category, generator) {
|
|
101
115
|
const blockDefinition = {
|
|
102
116
|
init: function () {
|
|
103
|
-
|
|
104
|
-
const rootInput = !inputConn
|
|
105
|
-
? this.appendDummyInput("dummyInput")
|
|
106
|
-
: this.appendValueInput(inputConn.name).setCheck(inputConn.type);
|
|
117
|
+
let rootInput = null;
|
|
107
118
|
|
|
108
119
|
for (const field of entry.blocklyTemplate) {
|
|
109
120
|
if (field.text) {
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
if (!rootInput) {
|
|
122
|
+
rootInput = this.appendDummyInput()
|
|
123
|
+
}
|
|
124
|
+
//split text by \n
|
|
125
|
+
//create a new dummy input for every new line
|
|
126
|
+
//then add the text as a field
|
|
127
|
+
const cleanText = field.text.split("\n")
|
|
128
|
+
for (let i = 0; i < cleanText.length; i++) {
|
|
129
|
+
rootInput.appendField(cleanText[i]);
|
|
130
|
+
|
|
131
|
+
if (i != cleanText.length - 1) {
|
|
132
|
+
rootInput = this.appendDummyInput()
|
|
133
|
+
}
|
|
134
|
+
}
|
|
112
135
|
} else if (field.field) {
|
|
136
|
+
if (!rootInput) {
|
|
137
|
+
rootInput = this.appendDummyInput()
|
|
138
|
+
}
|
|
113
139
|
// Additional user input
|
|
114
140
|
rootInput.appendField(field.field(), field.name);
|
|
141
|
+
} else if (field.blocklyInput) {
|
|
142
|
+
if (field.blocklyInput.type === "Any" || field.blocklyInput.type == null){
|
|
143
|
+
rootInput = this.appendValueInput(field.blocklyInput.name)
|
|
144
|
+
if (field.blocklyInput.shadow) {
|
|
145
|
+
const shadowXml = utils.xml.textToDom(`
|
|
146
|
+
<shadow type="${field.blocklyInput.shadow}">
|
|
147
|
+
</shadow>
|
|
148
|
+
`);
|
|
149
|
+
rootInput.connection.setShadowDom(shadowXml);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
rootInput = this.appendDummyInput() //needed to make sure next inputs go to after this one otheriwise they get added in front of this input
|
|
153
|
+
} else if (field.blocklyInput.type === "Void") {
|
|
154
|
+
rootInput = this.appendStatementInput(field.blocklyInput.name)
|
|
155
|
+
if (field.blocklyInput.shadow) {
|
|
156
|
+
const shadowXml = utils.xml.textToDom(`
|
|
157
|
+
<shadow type="${field.blocklyInput.shadow}">
|
|
158
|
+
</shadow>
|
|
159
|
+
`);
|
|
160
|
+
rootInput.connection.setShadowDom(shadowXml);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
rootInput = this.appendValueInput(field.blocklyInput.name).setCheck(field.blocklyInput.type || null)
|
|
164
|
+
if (field.blocklyInput.shadow) {
|
|
165
|
+
const shadowXml = utils.xml.textToDom(`
|
|
166
|
+
<shadow type="${field.blocklyInput.shadow}">
|
|
167
|
+
</shadow>
|
|
168
|
+
`);
|
|
169
|
+
rootInput.connection.setShadowDom(shadowXml);
|
|
170
|
+
}
|
|
171
|
+
rootInput = this.appendDummyInput() //needed to make sure next inputs go to after this one otheriwise they get added in front of this input
|
|
172
|
+
}
|
|
115
173
|
} else {
|
|
116
174
|
console.error("unknown field type: ", field);
|
|
117
175
|
}
|
|
118
176
|
}
|
|
119
177
|
|
|
178
|
+
this.setInputsInline(entry.inputsInline);
|
|
179
|
+
|
|
120
180
|
const outputConn = entry.blocklyOutput;
|
|
121
181
|
if (!outputConn) {
|
|
122
182
|
this.setPreviousStatement(true, null);
|
|
@@ -125,10 +185,21 @@ function initBlocklyBlock(entry, category, generator) {
|
|
|
125
185
|
this.setOutput(true, outputConn.type);
|
|
126
186
|
}
|
|
127
187
|
|
|
188
|
+
//TODO: figure out where to put this since every block will have the help button
|
|
189
|
+
rootInput.appendField(new FieldImage("./images/help.svg", 15, 15, "Info", () => console.log("Clicked!" + entry.name)), "info_icon");
|
|
190
|
+
|
|
128
191
|
this.setTooltip(entry.description || "");
|
|
129
192
|
this.setHelpUrl("");
|
|
130
193
|
this.setColour(category.color);
|
|
131
194
|
},
|
|
195
|
+
|
|
196
|
+
saveExtraState: entry.save, //used for saving extra state
|
|
197
|
+
loadExtraState: entry.load, //used for loading extra state
|
|
198
|
+
updateShape_: entry.update, //used for updating shape based on state
|
|
199
|
+
saveConnections: entry.saveConnections, //used for saving connections
|
|
200
|
+
compose: entry.compose, //used for composing block from saved connections
|
|
201
|
+
decompose: entry.decompose, //used for decomposing block to save connections
|
|
202
|
+
onchange: entry.onchange, //used for handling block changes
|
|
132
203
|
};
|
|
133
204
|
|
|
134
205
|
// Define the block globally
|
|
@@ -147,75 +218,8 @@ function initBlocklyBlock(entry, category, generator) {
|
|
|
147
218
|
* @returns {{kind: string, contents: object[]}} The blockly toolbox.
|
|
148
219
|
*/
|
|
149
220
|
function initBlocklyToolbox(blocklyCategories) {
|
|
150
|
-
// TODO: filter default categories
|
|
151
|
-
const defaultCategories = [
|
|
152
|
-
{
|
|
153
|
-
kind: "category",
|
|
154
|
-
name: "Flow",
|
|
155
|
-
colour: "#e9a719",
|
|
156
|
-
contents: [
|
|
157
|
-
{
|
|
158
|
-
kind: "block",
|
|
159
|
-
type: "controls_if",
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
kind: "block",
|
|
163
|
-
type: "logic_compare",
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
kind: "block",
|
|
167
|
-
type: "logic_operation",
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
kind: "block",
|
|
171
|
-
type: "logic_negate",
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
kind: "block",
|
|
175
|
-
type: "logic_boolean",
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
kind: "block",
|
|
179
|
-
type: "controls_repeat_ext",
|
|
180
|
-
inputs: {
|
|
181
|
-
TIMES: {
|
|
182
|
-
block: {
|
|
183
|
-
type: "math_number",
|
|
184
|
-
fields: {
|
|
185
|
-
NUM: 10,
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
kind: "block",
|
|
193
|
-
type: "controls_whileUntil",
|
|
194
|
-
},
|
|
195
|
-
],
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
kind: "category",
|
|
199
|
-
name: "Math",
|
|
200
|
-
colour: "#cc44cc",
|
|
201
|
-
contents: [
|
|
202
|
-
{
|
|
203
|
-
kind: "block",
|
|
204
|
-
type: "math_number",
|
|
205
|
-
fields: {
|
|
206
|
-
NUM: 123,
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
kind: "block",
|
|
211
|
-
type: "math_arithmetic",
|
|
212
|
-
},
|
|
213
|
-
],
|
|
214
|
-
},
|
|
215
|
-
];
|
|
216
|
-
|
|
217
221
|
return {
|
|
218
222
|
kind: "categoryToolbox",
|
|
219
|
-
contents: blocklyCategories
|
|
223
|
+
contents: blocklyCategories,
|
|
220
224
|
};
|
|
221
225
|
}
|