Skip to content

Commit 87df8e3

Browse files
maribethbcpcallen
andauthored
feat: publish new developer tools (#2398)
* feat: add basic infrastructure of developer-tools * chore: add eof newline where missing * fix: formatting, license, css attributes * feat: add block factory blocks and toolbox (#2155) * feat: add block factory blocks and toolbox * fix: pr comments * fix: change type to connection check * fix: formatting and comments * chore: lint and format developer-tools (#2185) * chore: autofix lint * chore: manually fix most of the lint * chore: format * feat: add basic saving and loading and start block state (#2187) * feat: add json definition generator (#2188) * feat: add javascript definition generators for blocks (#2196) * feat: add javascript definition generators for blocks * chore: format * fix: switch statement style Co-authored-by: Christopher Allen <[email protected]> * fix: update to latest blockly to use JavascriptGenerator class * fix: fix img dropdown option --------- Co-authored-by: Christopher Allen <[email protected]> * feat: add controllers and models to switch between definitions (#2219) * feat: add code header generation for imports and script tags (#2286) * feat: add save, load, and delete functionality to dev-tools (#2285) * feat: add save, load, and delete functionality to dev-tools * chore: format * chore: update load name * feat: add generator stubs to block factory (#2295) * feat: add generator stub generator and output * feat: add generator headers * chore: add more tsdoc * chore: minor refactoring of template strings * feat: save block factory settings (#2297) * feat: save block factory settings * chore: const to named functions * feat: add ability to convert old block factory json to new (#2304) * feat: add ability to convert old block factory json to new * chore: format and use constant * fix: use better typings, minor refactoring * feat: add shadow blocks for connection checks and real colour block (#2307) * feat: add file upload for block factory (#2320) * feat: add file upload for block factory * chore: fix questionable html formatting * chore: rename and comments * feat: add angle and colour fields to block factory (#2325) * feat: add angle and colour fields to block factory * fix: call register fields in script header * feat: support uploading file from old block factory (#2336) * feat: support uploading file from old block factory * feat: support multiple file input, minor pr fixes * feat: update to blockly v11 & improve style (#2388) * fix: styling * fix: changes for v11 * fix: set max height in narrow mode * fix: min height of code divs * chore: format * chore: remove log * feat: use a js legal name for the block in code output (#2392) * feat: use a js legal name for the block in code output * fix: legal js name probably * feat: add help button and favicon (#2396) * feat: include developer-tools when publishing to gh-pages (#2395) --------- Co-authored-by: Christopher Allen <[email protected]>
1 parent 679bf08 commit 87df8e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+23835
-2
lines changed

examples/developer-tools/package-lock.json

Lines changed: 18803 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"name": "developer-tools",
3+
"version": "1.0.0",
4+
"description": "Blockly Developer Tools",
5+
"private": true,
6+
"main": "index.js",
7+
"scripts": {
8+
"start": "webpack serve --open --mode=development",
9+
"build": "webpack --mode=production --node-env=production",
10+
"build:dev": "webpack --mode=development",
11+
"build:prod": "webpack --mode=production --node-env=production",
12+
"watch": "webpack --watch",
13+
"serve": "webpack serve",
14+
"predeploy": "npm run build:prod"
15+
},
16+
"repository": {
17+
"type": "git",
18+
"url": "git+https://github.com/google/blockly-samples.git"
19+
},
20+
"keywords": [
21+
"blockly"
22+
],
23+
"author": "Blockly Team",
24+
"license": "Apache-2.0",
25+
"bugs": {
26+
"url": "https://github.com/google/blockly-samples/issues"
27+
},
28+
"homepage": "https://github.com/google/blockly-samples#readme",
29+
"devDependencies": {
30+
"@webpack-cli/generators": "^3.0.7",
31+
"css-loader": "^6.8.1",
32+
"html-webpack-plugin": "^5.5.3",
33+
"mini-css-extract-plugin": "^2.7.6",
34+
"style-loader": "^3.3.3",
35+
"ts-loader": "^9.4.4",
36+
"typescript": "^5.1.6",
37+
"webpack": "^5.88.2",
38+
"webpack-cli": "^5.1.4",
39+
"webpack-dev-server": "^4.15.1"
40+
},
41+
"dependencies": {
42+
"@blockly/field-angle": "^5.0.2",
43+
"@blockly/field-colour": "^5.0.2",
44+
"@material/web": "^1.4.0",
45+
"blockly": "^11.1.1"
46+
}
47+
}
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/**
8+
* This file contains methods that can convert blocks saved in the old
9+
* block factory tool that was hosted on app engine, into blocks that
10+
* can be used with this new tool.
11+
*
12+
* Many of the blocks from the old tool are the same as the blocks
13+
* in this tool. But in some cases, block, field, or input names have
14+
* been changed for clarity. This file will edit block json so that
15+
* the saved data from the old tool can be loaded into this tool.
16+
*/
17+
18+
import * as Blockly from 'blockly/core';
19+
20+
/** Shadow state for a connection check block. */
21+
const CONNECTION_CHECK_SHADOW = {
22+
type: 'connection_check',
23+
fields: {
24+
CHECKDROPDOWN: 'null',
25+
},
26+
};
27+
28+
/**
29+
* The factory_base block is largely the same. However, the inputs that spawn
30+
* if the block has top/bottom/left connectors have been renamed from
31+
* `OUTPUTTYPE` to `OUTPUTCHECK`. The input blocks connected to this one
32+
* also need to be converted.
33+
*
34+
* @param oldBlock The JSON for the factory_base as saved from old tool.
35+
* @returns JSON that should be loaded instead.
36+
*/
37+
export function convertBaseBlock(
38+
oldBlock: Blockly.serialization.blocks.State,
39+
): Blockly.serialization.blocks.State {
40+
if (oldBlock.type !== 'factory_base') {
41+
throw Error('Malformed block data');
42+
}
43+
44+
const newBlock = {...oldBlock};
45+
// extraState from the old tool isn't relevant.
46+
delete newBlock.extraState;
47+
48+
if (oldBlock.inputs?.INPUTS?.block) {
49+
newBlock.inputs.INPUTS.block = convertInput(oldBlock.inputs.INPUTS.block);
50+
}
51+
52+
/**
53+
* Converts the top-level connection checks on the 'factory_base' block to the new level.
54+
*
55+
* @param connectionName Name of the input to create the connection check block for.
56+
*/
57+
function convertFactoryBaseConnectionChecks(
58+
connectionName: 'OUTPUT' | 'TOP' | 'BOTTOM',
59+
) {
60+
// The names of the input on the old and new blocks
61+
// e.g. 'OUTPUTTYPE' vs 'OUTPUTCHECK'
62+
const oldInputName = connectionName + 'TYPE';
63+
const newInputName = connectionName + 'CHECK';
64+
65+
// If this input didn't exist in the old block, it also doesn't
66+
// exist on the new block. Nothing to do here.
67+
if (!oldBlock.inputs || !oldBlock.inputs[oldInputName]) return;
68+
69+
const newCheckInput: Blockly.serialization.blocks.ConnectionState = {};
70+
// Shadow block always exists.
71+
newCheckInput.shadow = CONNECTION_CHECK_SHADOW;
72+
if (oldBlock.inputs[oldInputName].block) {
73+
// Real block only exists if it existed in the old block too.
74+
newCheckInput.block = convertCheck(oldBlock.inputs[oldInputName].block);
75+
}
76+
newBlock.inputs[newInputName] = newCheckInput;
77+
// New block doesn't need the input with the old name.
78+
delete newBlock.inputs[oldInputName];
79+
}
80+
81+
convertFactoryBaseConnectionChecks('OUTPUT');
82+
convertFactoryBaseConnectionChecks('BOTTOM');
83+
convertFactoryBaseConnectionChecks('TOP');
84+
85+
return newBlock;
86+
}
87+
88+
/**
89+
* The input blocks are different. In the old tool, each type of input had its own
90+
* block definition. In this tool, there is one "input" block that has a dropdown
91+
* to select an input type. Also, the old blocks have a connection "type" while
92+
* the new blocks have a connection "check".
93+
*
94+
* @param oldBlock JSON for the "input_foo" block as saved from old tool.
95+
* @returns JSON that should be used for the replacement "input" block.
96+
*/
97+
function convertInput(
98+
oldBlock: Blockly.serialization.blocks.State,
99+
): Blockly.serialization.blocks.State {
100+
const newBlock: Blockly.serialization.blocks.State = {
101+
type: 'input',
102+
fields: {
103+
INPUTTYPE: oldBlock.type,
104+
},
105+
inputs: {},
106+
};
107+
108+
if (oldBlock.fields?.ALIGN) {
109+
// Note new name in new tool.
110+
newBlock.fields.ALIGNMENT = oldBlock.fields?.ALIGN;
111+
}
112+
113+
if (oldBlock.fields?.INPUTNAME) {
114+
newBlock.fields.INPUTNAME = oldBlock.fields.INPUTNAME;
115+
}
116+
117+
if (oldBlock.inputs?.TYPE) {
118+
newBlock.inputs.CHECK = {
119+
shadow: CONNECTION_CHECK_SHADOW,
120+
};
121+
if (oldBlock.inputs.TYPE.block) {
122+
newBlock.inputs.CHECK.block = convertCheck(oldBlock.inputs.TYPE.block);
123+
}
124+
}
125+
126+
if (oldBlock.inputs?.FIELDS?.block) {
127+
newBlock.inputs.FIELDS = {
128+
block: convertField(oldBlock.inputs.FIELDS.block),
129+
};
130+
}
131+
132+
if (oldBlock.next?.block) {
133+
newBlock.next = {
134+
block: convertInput(oldBlock.next.block),
135+
};
136+
}
137+
return newBlock;
138+
}
139+
140+
/**
141+
* The field blocks are all mostly the same, with a few exceptions:
142+
* "field_static" in the old tool is called "field_label" here.
143+
* "field_dropdown"'s extraState uses xml in the old tool and json in the new tool.
144+
*
145+
* TODO(#2290): Check for backwards-compatibility issues with plugin fields.
146+
*
147+
* @param oldBlock JSON for the "field_foo" block as saved from old tool.
148+
* @returns JSON that should be used for the replacement field block.
149+
*/
150+
function convertField(
151+
oldBlock: Blockly.serialization.blocks.State,
152+
): Blockly.serialization.blocks.State {
153+
const newBlock = {...oldBlock};
154+
if (oldBlock.type === 'field_static') {
155+
newBlock.type = 'field_label';
156+
}
157+
158+
if (oldBlock.type === 'field_dropdown' && oldBlock.extraState) {
159+
const extraState = Blockly.utils.xml.textToDom(oldBlock.extraState);
160+
const options = JSON.parse(extraState.getAttribute('options'));
161+
newBlock.extraState = {
162+
options: options,
163+
};
164+
}
165+
if (oldBlock.next?.block) {
166+
newBlock.next.block = convertField(oldBlock.next.block);
167+
}
168+
return newBlock;
169+
}
170+
171+
/**
172+
* The type/check blocks are different. In the old tool, each "type"
173+
* (e.g. Number or Boolean) had its own block definition. In this
174+
* tool, there is one "connection_check" block that has a dropdown
175+
* to select a check. We prefer the term "check" to "type" in all cases,
176+
* to match documentation and reduce the confusion between multiple meanings of "type".
177+
*
178+
* @param oldBlock JSON for the "type_foo" block as saved from old tool.
179+
* @returns JSON that should be used for the replacement "connection_check" block.
180+
*/
181+
function convertCheck(
182+
oldBlock: Blockly.serialization.blocks.State,
183+
): Blockly.serialization.blocks.State {
184+
const oldName = oldBlock.type; // The block type i.e. name of block definition
185+
if (!oldName.startsWith('type_')) {
186+
throw Error(
187+
`Found connection check block with unexpected block type ${oldName}`,
188+
);
189+
}
190+
let connectionCheck = oldName.substring(5);
191+
switch (connectionCheck) {
192+
case 'null':
193+
// check value does not change if 'null'
194+
break;
195+
case 'boolean':
196+
connectionCheck = 'Boolean';
197+
break;
198+
case 'number':
199+
connectionCheck = 'Number';
200+
break;
201+
case 'string':
202+
connectionCheck = 'String';
203+
break;
204+
case 'list':
205+
connectionCheck = 'Array';
206+
break;
207+
case 'other':
208+
return convertCustomCheck(oldBlock);
209+
case 'group':
210+
return convertGroupCheck(oldBlock);
211+
default:
212+
throw Error(
213+
`Found connection check block with unexpected type: ${connectionCheck}`,
214+
);
215+
}
216+
return {
217+
type: 'connection_check',
218+
fields: {
219+
CHECKDROPDOWN: connectionCheck,
220+
},
221+
};
222+
}
223+
224+
/**
225+
* Converts an old "type_other" block into a "check" block with custom value.
226+
*
227+
* @param oldBlock JSON for the "type_other" block as saved from old tool.
228+
* @returns JSON that should be used for the replacement "connection_check" block.
229+
*/
230+
function convertCustomCheck(
231+
oldBlock: Blockly.serialization.blocks.State,
232+
): Blockly.serialization.blocks.State {
233+
const customCheck = oldBlock.fields.TYPE;
234+
return {
235+
type: 'connection_check',
236+
extraState: {
237+
customCheck: customCheck,
238+
},
239+
fields: {
240+
CHECKDROPDOWN: 'CUSTOM',
241+
CUSTOMCHECK: customCheck,
242+
},
243+
};
244+
}
245+
246+
/**
247+
* Converts an old "type_group" block into a "connection_check_group" block.
248+
* The old block has inputs named `TYPE0`, `TYPE1`, etc. The new block renames
249+
* these inputs to `CHECK0`, `CHECK1`, etc.
250+
*
251+
* @param oldBlock JSON for the "type_group" block as saved from old tool.
252+
* @returns JSON that should be used for the replacement "connection_check" block.
253+
*/
254+
function convertGroupCheck(
255+
oldBlock: Blockly.serialization.blocks.State,
256+
): Blockly.serialization.blocks.State {
257+
const inputs: Record<string, object> = {};
258+
const checkCount = parseInt(
259+
Blockly.utils.xml.textToDom(oldBlock.extraState).getAttribute('types'),
260+
);
261+
for (let index = 0; index < checkCount; index++) {
262+
if (oldBlock.inputs['TYPE' + index]?.block) {
263+
inputs['CHECK' + index] = {
264+
block: convertCheck(oldBlock.inputs['TYPE' + index].block),
265+
};
266+
}
267+
}
268+
return {
269+
type: 'connection_check_group',
270+
inputs: inputs,
271+
extraState: {
272+
checkCount: checkCount,
273+
},
274+
};
275+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {FieldAngle} from '@blockly/field-angle';
8+
9+
/**
10+
* A hue-picker to set the colour of a block.
11+
* TODO(#2159): Use the new angle field.
12+
*/
13+
export const colourHue = {
14+
init: function () {
15+
this.appendDummyInput()
16+
.appendField('hue:')
17+
.appendField(new FieldAngle('0', this.updateBlockColour), 'HUE');
18+
this.setOutput(true, 'Colour');
19+
this.setTooltip('Paint the block with this colour.');
20+
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=55');
21+
},
22+
updateBlockColour: function (hue: number) {
23+
this.getSourceBlock().setColour(hue);
24+
},
25+
};

0 commit comments

Comments
 (0)