-
Notifications
You must be signed in to change notification settings - Fork 626
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <cpcallen+github@gmail.com> * fix: update to latest blockly to use JavascriptGenerator class * fix: fix img dropdown option --------- Co-authored-by: Christopher Allen <cpcallen+github@gmail.com> * 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 <cpcallen+github@gmail.com>
- Loading branch information
Showing
42 changed files
with
23,835 additions
and
2 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{ | ||
"name": "developer-tools", | ||
"version": "1.0.0", | ||
"description": "Blockly Developer Tools", | ||
"private": true, | ||
"main": "index.js", | ||
"scripts": { | ||
"start": "webpack serve --open --mode=development", | ||
"build": "webpack --mode=production --node-env=production", | ||
"build:dev": "webpack --mode=development", | ||
"build:prod": "webpack --mode=production --node-env=production", | ||
"watch": "webpack --watch", | ||
"serve": "webpack serve", | ||
"predeploy": "npm run build:prod" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/google/blockly-samples.git" | ||
}, | ||
"keywords": [ | ||
"blockly" | ||
], | ||
"author": "Blockly Team", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/google/blockly-samples/issues" | ||
}, | ||
"homepage": "https://github.com/google/blockly-samples#readme", | ||
"devDependencies": { | ||
"@webpack-cli/generators": "^3.0.7", | ||
"css-loader": "^6.8.1", | ||
"html-webpack-plugin": "^5.5.3", | ||
"mini-css-extract-plugin": "^2.7.6", | ||
"style-loader": "^3.3.3", | ||
"ts-loader": "^9.4.4", | ||
"typescript": "^5.1.6", | ||
"webpack": "^5.88.2", | ||
"webpack-cli": "^5.1.4", | ||
"webpack-dev-server": "^4.15.1" | ||
}, | ||
"dependencies": { | ||
"@blockly/field-angle": "^5.0.2", | ||
"@blockly/field-colour": "^5.0.2", | ||
"@material/web": "^1.4.0", | ||
"blockly": "^11.1.1" | ||
} | ||
} |
275 changes: 275 additions & 0 deletions
275
examples/developer-tools/src/backwards_compatibility.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,275 @@ | ||
/** | ||
* @license | ||
* Copyright 2024 Google LLC | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
/** | ||
* This file contains methods that can convert blocks saved in the old | ||
* block factory tool that was hosted on app engine, into blocks that | ||
* can be used with this new tool. | ||
* | ||
* Many of the blocks from the old tool are the same as the blocks | ||
* in this tool. But in some cases, block, field, or input names have | ||
* been changed for clarity. This file will edit block json so that | ||
* the saved data from the old tool can be loaded into this tool. | ||
*/ | ||
|
||
import * as Blockly from 'blockly/core'; | ||
|
||
/** Shadow state for a connection check block. */ | ||
const CONNECTION_CHECK_SHADOW = { | ||
type: 'connection_check', | ||
fields: { | ||
CHECKDROPDOWN: 'null', | ||
}, | ||
}; | ||
|
||
/** | ||
* The factory_base block is largely the same. However, the inputs that spawn | ||
* if the block has top/bottom/left connectors have been renamed from | ||
* `OUTPUTTYPE` to `OUTPUTCHECK`. The input blocks connected to this one | ||
* also need to be converted. | ||
* | ||
* @param oldBlock The JSON for the factory_base as saved from old tool. | ||
* @returns JSON that should be loaded instead. | ||
*/ | ||
export function convertBaseBlock( | ||
oldBlock: Blockly.serialization.blocks.State, | ||
): Blockly.serialization.blocks.State { | ||
if (oldBlock.type !== 'factory_base') { | ||
throw Error('Malformed block data'); | ||
} | ||
|
||
const newBlock = {...oldBlock}; | ||
// extraState from the old tool isn't relevant. | ||
delete newBlock.extraState; | ||
|
||
if (oldBlock.inputs?.INPUTS?.block) { | ||
newBlock.inputs.INPUTS.block = convertInput(oldBlock.inputs.INPUTS.block); | ||
} | ||
|
||
/** | ||
* Converts the top-level connection checks on the 'factory_base' block to the new level. | ||
* | ||
* @param connectionName Name of the input to create the connection check block for. | ||
*/ | ||
function convertFactoryBaseConnectionChecks( | ||
connectionName: 'OUTPUT' | 'TOP' | 'BOTTOM', | ||
) { | ||
// The names of the input on the old and new blocks | ||
// e.g. 'OUTPUTTYPE' vs 'OUTPUTCHECK' | ||
const oldInputName = connectionName + 'TYPE'; | ||
const newInputName = connectionName + 'CHECK'; | ||
|
||
// If this input didn't exist in the old block, it also doesn't | ||
// exist on the new block. Nothing to do here. | ||
if (!oldBlock.inputs || !oldBlock.inputs[oldInputName]) return; | ||
|
||
const newCheckInput: Blockly.serialization.blocks.ConnectionState = {}; | ||
// Shadow block always exists. | ||
newCheckInput.shadow = CONNECTION_CHECK_SHADOW; | ||
if (oldBlock.inputs[oldInputName].block) { | ||
// Real block only exists if it existed in the old block too. | ||
newCheckInput.block = convertCheck(oldBlock.inputs[oldInputName].block); | ||
} | ||
newBlock.inputs[newInputName] = newCheckInput; | ||
// New block doesn't need the input with the old name. | ||
delete newBlock.inputs[oldInputName]; | ||
} | ||
|
||
convertFactoryBaseConnectionChecks('OUTPUT'); | ||
convertFactoryBaseConnectionChecks('BOTTOM'); | ||
convertFactoryBaseConnectionChecks('TOP'); | ||
|
||
return newBlock; | ||
} | ||
|
||
/** | ||
* The input blocks are different. In the old tool, each type of input had its own | ||
* block definition. In this tool, there is one "input" block that has a dropdown | ||
* to select an input type. Also, the old blocks have a connection "type" while | ||
* the new blocks have a connection "check". | ||
* | ||
* @param oldBlock JSON for the "input_foo" block as saved from old tool. | ||
* @returns JSON that should be used for the replacement "input" block. | ||
*/ | ||
function convertInput( | ||
oldBlock: Blockly.serialization.blocks.State, | ||
): Blockly.serialization.blocks.State { | ||
const newBlock: Blockly.serialization.blocks.State = { | ||
type: 'input', | ||
fields: { | ||
INPUTTYPE: oldBlock.type, | ||
}, | ||
inputs: {}, | ||
}; | ||
|
||
if (oldBlock.fields?.ALIGN) { | ||
// Note new name in new tool. | ||
newBlock.fields.ALIGNMENT = oldBlock.fields?.ALIGN; | ||
} | ||
|
||
if (oldBlock.fields?.INPUTNAME) { | ||
newBlock.fields.INPUTNAME = oldBlock.fields.INPUTNAME; | ||
} | ||
|
||
if (oldBlock.inputs?.TYPE) { | ||
newBlock.inputs.CHECK = { | ||
shadow: CONNECTION_CHECK_SHADOW, | ||
}; | ||
if (oldBlock.inputs.TYPE.block) { | ||
newBlock.inputs.CHECK.block = convertCheck(oldBlock.inputs.TYPE.block); | ||
} | ||
} | ||
|
||
if (oldBlock.inputs?.FIELDS?.block) { | ||
newBlock.inputs.FIELDS = { | ||
block: convertField(oldBlock.inputs.FIELDS.block), | ||
}; | ||
} | ||
|
||
if (oldBlock.next?.block) { | ||
newBlock.next = { | ||
block: convertInput(oldBlock.next.block), | ||
}; | ||
} | ||
return newBlock; | ||
} | ||
|
||
/** | ||
* The field blocks are all mostly the same, with a few exceptions: | ||
* "field_static" in the old tool is called "field_label" here. | ||
* "field_dropdown"'s extraState uses xml in the old tool and json in the new tool. | ||
* | ||
* TODO(#2290): Check for backwards-compatibility issues with plugin fields. | ||
* | ||
* @param oldBlock JSON for the "field_foo" block as saved from old tool. | ||
* @returns JSON that should be used for the replacement field block. | ||
*/ | ||
function convertField( | ||
oldBlock: Blockly.serialization.blocks.State, | ||
): Blockly.serialization.blocks.State { | ||
const newBlock = {...oldBlock}; | ||
if (oldBlock.type === 'field_static') { | ||
newBlock.type = 'field_label'; | ||
} | ||
|
||
if (oldBlock.type === 'field_dropdown' && oldBlock.extraState) { | ||
const extraState = Blockly.utils.xml.textToDom(oldBlock.extraState); | ||
const options = JSON.parse(extraState.getAttribute('options')); | ||
newBlock.extraState = { | ||
options: options, | ||
}; | ||
} | ||
if (oldBlock.next?.block) { | ||
newBlock.next.block = convertField(oldBlock.next.block); | ||
} | ||
return newBlock; | ||
} | ||
|
||
/** | ||
* The type/check blocks are different. In the old tool, each "type" | ||
* (e.g. Number or Boolean) had its own block definition. In this | ||
* tool, there is one "connection_check" block that has a dropdown | ||
* to select a check. We prefer the term "check" to "type" in all cases, | ||
* to match documentation and reduce the confusion between multiple meanings of "type". | ||
* | ||
* @param oldBlock JSON for the "type_foo" block as saved from old tool. | ||
* @returns JSON that should be used for the replacement "connection_check" block. | ||
*/ | ||
function convertCheck( | ||
oldBlock: Blockly.serialization.blocks.State, | ||
): Blockly.serialization.blocks.State { | ||
const oldName = oldBlock.type; // The block type i.e. name of block definition | ||
if (!oldName.startsWith('type_')) { | ||
throw Error( | ||
`Found connection check block with unexpected block type ${oldName}`, | ||
); | ||
} | ||
let connectionCheck = oldName.substring(5); | ||
switch (connectionCheck) { | ||
case 'null': | ||
// check value does not change if 'null' | ||
break; | ||
case 'boolean': | ||
connectionCheck = 'Boolean'; | ||
break; | ||
case 'number': | ||
connectionCheck = 'Number'; | ||
break; | ||
case 'string': | ||
connectionCheck = 'String'; | ||
break; | ||
case 'list': | ||
connectionCheck = 'Array'; | ||
break; | ||
case 'other': | ||
return convertCustomCheck(oldBlock); | ||
case 'group': | ||
return convertGroupCheck(oldBlock); | ||
default: | ||
throw Error( | ||
`Found connection check block with unexpected type: ${connectionCheck}`, | ||
); | ||
} | ||
return { | ||
type: 'connection_check', | ||
fields: { | ||
CHECKDROPDOWN: connectionCheck, | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Converts an old "type_other" block into a "check" block with custom value. | ||
* | ||
* @param oldBlock JSON for the "type_other" block as saved from old tool. | ||
* @returns JSON that should be used for the replacement "connection_check" block. | ||
*/ | ||
function convertCustomCheck( | ||
oldBlock: Blockly.serialization.blocks.State, | ||
): Blockly.serialization.blocks.State { | ||
const customCheck = oldBlock.fields.TYPE; | ||
return { | ||
type: 'connection_check', | ||
extraState: { | ||
customCheck: customCheck, | ||
}, | ||
fields: { | ||
CHECKDROPDOWN: 'CUSTOM', | ||
CUSTOMCHECK: customCheck, | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Converts an old "type_group" block into a "connection_check_group" block. | ||
* The old block has inputs named `TYPE0`, `TYPE1`, etc. The new block renames | ||
* these inputs to `CHECK0`, `CHECK1`, etc. | ||
* | ||
* @param oldBlock JSON for the "type_group" block as saved from old tool. | ||
* @returns JSON that should be used for the replacement "connection_check" block. | ||
*/ | ||
function convertGroupCheck( | ||
oldBlock: Blockly.serialization.blocks.State, | ||
): Blockly.serialization.blocks.State { | ||
const inputs: Record<string, object> = {}; | ||
const checkCount = parseInt( | ||
Blockly.utils.xml.textToDom(oldBlock.extraState).getAttribute('types'), | ||
); | ||
for (let index = 0; index < checkCount; index++) { | ||
if (oldBlock.inputs['TYPE' + index]?.block) { | ||
inputs['CHECK' + index] = { | ||
block: convertCheck(oldBlock.inputs['TYPE' + index].block), | ||
}; | ||
} | ||
} | ||
return { | ||
type: 'connection_check_group', | ||
inputs: inputs, | ||
extraState: { | ||
checkCount: checkCount, | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* @license | ||
* Copyright 2024 Google LLC | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import {FieldAngle} from '@blockly/field-angle'; | ||
|
||
/** | ||
* A hue-picker to set the colour of a block. | ||
* TODO(#2159): Use the new angle field. | ||
*/ | ||
export const colourHue = { | ||
init: function () { | ||
this.appendDummyInput() | ||
.appendField('hue:') | ||
.appendField(new FieldAngle('0', this.updateBlockColour), 'HUE'); | ||
this.setOutput(true, 'Colour'); | ||
this.setTooltip('Paint the block with this colour.'); | ||
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=55'); | ||
}, | ||
updateBlockColour: function (hue: number) { | ||
this.getSourceBlock().setColour(hue); | ||
}, | ||
}; |
Oops, something went wrong.