From 052226a1d22d38e47b05ef9faff0d58b39f1d6f4 Mon Sep 17 00:00:00 2001 From: heliacer Date: Fri, 12 Sep 2025 19:30:30 +0200 Subject: [PATCH 01/12] fix: update CSS selector for comment text styling in zelos ConstantProvider --- core/renderers/zelos/constants.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/renderers/zelos/constants.ts b/core/renderers/zelos/constants.ts index 8cd36e02589..e035b6681a9 100644 --- a/core/renderers/zelos/constants.ts +++ b/core/renderers/zelos/constants.ts @@ -825,7 +825,8 @@ export class ConstantProvider extends BaseConstantProvider { `pt ${this.FIELD_TEXT_FONTFAMILY};`, `}`, - `${selector} .blocklyTextInputBubble textarea {`, + // Comments. + `${selector} .blocklyCommentText {`, `font-weight: normal;`, `}`, From a08da6577f2f929002bba7ae636d725830feb609 Mon Sep 17 00:00:00 2001 From: heliacer Date: Fri, 26 Sep 2025 12:28:31 +0200 Subject: [PATCH 02/12] add interface, add neccessary output: null to missing json defs, not sure if it works --- blocks/logic.ts | 5 ++ blocks/loops.ts | 6 ++ blocks/math.ts | 1 + blocks/text.ts | 4 + blocks/variables.ts | 1 + blocks/variables_dynamic.ts | 1 + core/block.ts | 94 ++++++++++----------- core/common.ts | 11 +-- core/interfaces/i_json_block_definition.ts | 97 ++++++++++++++++++++++ 9 files changed, 164 insertions(+), 56 deletions(-) create mode 100644 core/interfaces/i_json_block_definition.ts diff --git a/blocks/logic.ts b/blocks/logic.ts index d2a7405fffa..6cbcd83a1f9 100644 --- a/blocks/logic.ts +++ b/blocks/logic.ts @@ -64,6 +64,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'DO0', }, ], + 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'logic_blocks', @@ -97,6 +98,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'ELSE', }, ], + 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'logic_blocks', @@ -228,6 +230,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'type': 'controls_if_if', 'message0': '%{BKY_CONTROLS_IF_IF_TITLE_IF}', 'nextStatement': null, + 'output': null, 'enableContextMenu': false, 'style': 'logic_blocks', 'tooltip': '%{BKY_CONTROLS_IF_IF_TOOLTIP}', @@ -238,6 +241,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'message0': '%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}', 'previousStatement': null, 'nextStatement': null, + 'output': null, 'enableContextMenu': false, 'style': 'logic_blocks', 'tooltip': '%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}', @@ -247,6 +251,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'type': 'controls_if_else', 'message0': '%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}', 'previousStatement': null, + 'output': null, 'enableContextMenu': false, 'style': 'logic_blocks', 'tooltip': '%{BKY_CONTROLS_IF_ELSE_TOOLTIP}', diff --git a/blocks/loops.ts b/blocks/loops.ts index 6d450e53215..a79b2ff8a45 100644 --- a/blocks/loops.ts +++ b/blocks/loops.ts @@ -53,6 +53,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'loop_blocks', 'tooltip': '%{BKY_CONTROLS_REPEAT_TOOLTIP}', 'helpUrl': '%{BKY_CONTROLS_REPEAT_HELPURL}', @@ -80,6 +81,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'loop_blocks', 'tooltip': '%{BKY_CONTROLS_REPEAT_TOOLTIP}', 'helpUrl': '%{BKY_CONTROLS_REPEAT_HELPURL}', @@ -112,6 +114,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_WHILEUNTIL_HELPURL}', 'extensions': ['controls_whileUntil_tooltip'], @@ -155,6 +158,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'inputsInline': true, 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_FOR_HELPURL}', 'extensions': ['contextMenu_newGetVariableBlock', 'controls_for_tooltip'], @@ -184,6 +188,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_FOREACH_HELPURL}', 'extensions': [ @@ -206,6 +211,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ }, ], 'previousStatement': null, + 'output': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}', 'suppressPrefixSuffix': true, diff --git a/blocks/math.ts b/blocks/math.ts index e5aef5fbb6e..238148e0689 100644 --- a/blocks/math.ts +++ b/blocks/math.ts @@ -208,6 +208,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'variable_blocks', 'helpUrl': '%{BKY_MATH_CHANGE_HELPURL}', 'extensions': ['math_change_tooltip'], diff --git a/blocks/text.ts b/blocks/text.ts index a7ad5374ac4..458a9bab7b2 100644 --- a/blocks/text.ts +++ b/blocks/text.ts @@ -68,6 +68,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'STACK', }, ], + 'output': null, 'style': 'text_blocks', 'tooltip': '%{BKY_TEXT_CREATE_JOIN_TOOLTIP}', 'enableContextMenu': false, @@ -78,6 +79,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'previousStatement': null, 'nextStatement': null, 'style': 'text_blocks', + 'output': null, 'tooltip': '%{BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}', 'enableContextMenu': false, }, @@ -97,6 +99,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'text_blocks', 'extensions': ['text_append_tooltip'], }, @@ -387,6 +390,7 @@ blocks['text_print'] = { 'name': 'TEXT', }, ], + 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'text_blocks', diff --git a/blocks/variables.ts b/blocks/variables.ts index 4f1f640fa81..e2bf96574f3 100644 --- a/blocks/variables.ts +++ b/blocks/variables.ts @@ -60,6 +60,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'variable_blocks', 'tooltip': '%{BKY_VARIABLES_SET_TOOLTIP}', 'helpUrl': '%{BKY_VARIABLES_SET_HELPURL}', diff --git a/blocks/variables_dynamic.ts b/blocks/variables_dynamic.ts index 8afd24cf2e3..d2944115d05 100644 --- a/blocks/variables_dynamic.ts +++ b/blocks/variables_dynamic.ts @@ -59,6 +59,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'VALUE', }, ], + 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'variable_dynamic_blocks', diff --git a/core/block.ts b/core/block.ts index af44facda5d..7070129705a 100644 --- a/core/block.ts +++ b/core/block.ts @@ -54,6 +54,7 @@ import * as idGenerator from './utils/idgenerator.js'; import * as parsing from './utils/parsing.js'; import {Size} from './utils/size.js'; import type {Workspace} from './workspace.js'; +import { BlockArg, JsonBlockDefinition } from './interfaces/i_json_block_definition.js' /** * Class for one block. @@ -1709,11 +1710,11 @@ export class Block { * * @param json Structured data describing the block. */ - jsonInit(json: AnyDuringMigration) { - const warningPrefix = json['type'] ? 'Block "' + json['type'] + '": ' : ''; + jsonInit(json: JsonBlockDefinition) { + const warningPrefix = json.type ? 'Block "' + json.type + '": ' : ''; // Validate inputs. - if (json['output'] && json['previousStatement']) { + if (json.output && json.previousStatement) { throw Error( warningPrefix + 'Must not have both an output and a previousStatement.', ); @@ -1721,8 +1722,8 @@ export class Block { // Validate that each arg has a corresponding message let n = 0; - while (json['args' + n]) { - if (json['message' + n] === undefined) { + while (json[`args${n}`]) { + if (json[`message${n}`] === undefined) { throw Error( warningPrefix + `args${n} must have a corresponding message (message${n}).`, @@ -1732,17 +1733,9 @@ export class Block { } // Set basic properties of block. - // Makes styles backward compatible with old way of defining hat style. - if (json['style'] && json['style'].hat) { - this.hat = json['style'].hat; - // Must set to null so it doesn't error when checking for style and - // colour. - json['style'] = null; - } - - if (json['style'] && json['colour']) { + if (json.style && json.colour) { throw Error(warningPrefix + 'Must not have both a colour and a style.'); - } else if (json['style']) { + } else if (json.style) { this.jsonInitStyle(json, warningPrefix); } else { this.jsonInitColour(json, warningPrefix); @@ -1750,69 +1743,68 @@ export class Block { // Interpolate the message blocks. let i = 0; - while (json['message' + i] !== undefined) { + while (json[`message${i}`] !== undefined) { this.interpolate( - json['message' + i], - json['args' + i] || [], - // Backwards compatibility: lastDummyAlign aliases implicitAlign. - json['implicitAlign' + i] || json['lastDummyAlign' + i], + json[`message${i}`]!, + json[`args${i}`] || [], + json[`implicitAlign${i}`], warningPrefix, ); i++; } - if (json['inputsInline'] !== undefined) { + if (json.inputsInline !== undefined) { eventUtils.disable(); - this.setInputsInline(json['inputsInline']); + this.setInputsInline(json.inputsInline); eventUtils.enable(); } // Set output and previous/next connections. - if (json['output'] !== undefined) { - this.setOutput(true, json['output']); + if (json.output !== undefined) { + this.setOutput(true, json.output); } - if (json['outputShape'] !== undefined) { - this.setOutputShape(json['outputShape']); + if (json.outputShape !== undefined) { + this.setOutputShape(json.outputShape); } - if (json['previousStatement'] !== undefined) { - this.setPreviousStatement(true, json['previousStatement']); + if (json.previousStatement !== undefined) { + this.setPreviousStatement(true, json.previousStatement); } - if (json['nextStatement'] !== undefined) { - this.setNextStatement(true, json['nextStatement']); + if (json.nextStatement !== undefined) { + this.setNextStatement(true, json.nextStatement); } - if (json['tooltip'] !== undefined) { - const rawValue = json['tooltip']; + if (json.tooltip !== undefined) { + const rawValue = json.tooltip; const localizedText = parsing.replaceMessageReferences(rawValue); this.setTooltip(localizedText); } - if (json['enableContextMenu'] !== undefined) { - this.contextMenu = !!json['enableContextMenu']; + if (json.enableContextMenu !== undefined) { + this.contextMenu = !!json.enableContextMenu; } - if (json['suppressPrefixSuffix'] !== undefined) { - this.suppressPrefixSuffix = !!json['suppressPrefixSuffix']; + if (json.suppressPrefixSuffix !== undefined) { + this.suppressPrefixSuffix = !!json.suppressPrefixSuffix; } - if (json['helpUrl'] !== undefined) { - const rawValue = json['helpUrl']; + if (json.helpUrl !== undefined) { + const rawValue = json.helpUrl; const localizedValue = parsing.replaceMessageReferences(rawValue); this.setHelpUrl(localizedValue); } - if (typeof json['extensions'] === 'string') { + if (typeof json.extensions === 'string') { console.warn( warningPrefix + "JSON attribute 'extensions' should be an array of" + " strings. Found raw string in JSON for '" + - json['type'] + + json.type + "' block.", ); - json['extensions'] = [json['extensions']]; // Correct and continue. + json.extensions = [json.extensions]; // Correct and continue. } // Add the mutator to the block. - if (json['mutator'] !== undefined) { - Extensions.apply(json['mutator'], this, true); + if (json.mutator !== undefined) { + Extensions.apply(json.mutator, this, true); } - const extensionNames = json['extensions']; + const extensionNames = json.extensions; if (Array.isArray(extensionNames)) { for (let j = 0; j < extensionNames.length; j++) { Extensions.apply(extensionNames[j], this, false); @@ -1826,12 +1818,12 @@ export class Block { * @param json Structured data describing the block. * @param warningPrefix Warning prefix string identifying block. */ - private jsonInitColour(json: AnyDuringMigration, warningPrefix: string) { - if ('colour' in json) { - if (json['colour'] === undefined) { + private jsonInitColour(json: JsonBlockDefinition, warningPrefix: string) { + if (json.colour) { + if (json.colour === undefined) { console.warn(warningPrefix + 'Undefined colour value.'); } else { - const rawValue = json['colour']; + const rawValue = json.colour; try { this.setColour(rawValue); } catch { @@ -1847,8 +1839,8 @@ export class Block { * @param json Structured data describing the block. * @param warningPrefix Warning prefix string identifying block. */ - private jsonInitStyle(json: AnyDuringMigration, warningPrefix: string) { - const blockStyleName = json['style']; + private jsonInitStyle(json: JsonBlockDefinition, warningPrefix: string) { + const blockStyleName = json.style! try { this.setStyle(blockStyleName); } catch { @@ -1901,7 +1893,7 @@ export class Block { */ private interpolate( message: string, - args: AnyDuringMigration[], + args: BlockArg[], implicitAlign: string | undefined, warningPrefix: string, ) { diff --git a/core/common.ts b/core/common.ts index 7f23779ec93..08882e9bad2 100644 --- a/core/common.ts +++ b/core/common.ts @@ -13,6 +13,7 @@ import type {Connection} from './connection.js'; import {EventType} from './events/type.js'; import * as eventUtils from './events/utils.js'; import {getFocusManager} from './focus_manager.js'; +import {JsonBlockDefinition} from './interfaces/i_json_block_definition.js'; import {ISelectable, isSelectable} from './interfaces/i_selectable.js'; import {ShortcutRegistry} from './shortcut_registry.js'; import type {Workspace} from './workspace.js'; @@ -238,7 +239,7 @@ export function getBlockTypeCounts( * @returns A function that calls jsonInit with the correct value * of jsonDef. */ -function jsonInitFactory(jsonDef: AnyDuringMigration): () => void { +function jsonInitFactory(jsonDef: JsonBlockDefinition): () => void { return function (this: Block) { this.jsonInit(jsonDef); }; @@ -250,14 +251,14 @@ function jsonInitFactory(jsonDef: AnyDuringMigration): () => void { * * @param jsonArray An array of JSON block definitions. */ -export function defineBlocksWithJsonArray(jsonArray: AnyDuringMigration[]) { +export function defineBlocksWithJsonArray(jsonArray: JsonBlockDefinition[]) { TEST_ONLY.defineBlocksWithJsonArrayInternal(jsonArray); } /** * Private version of defineBlocksWithJsonArray for stubbing in tests. */ -function defineBlocksWithJsonArrayInternal(jsonArray: AnyDuringMigration[]) { +function defineBlocksWithJsonArrayInternal(jsonArray: JsonBlockDefinition[]) { defineBlocks(createBlockDefinitionsFromJsonArray(jsonArray)); } @@ -270,7 +271,7 @@ function defineBlocksWithJsonArrayInternal(jsonArray: AnyDuringMigration[]) { * definitions created. */ export function createBlockDefinitionsFromJsonArray( - jsonArray: AnyDuringMigration[], + jsonArray: JsonBlockDefinition[], ): {[key: string]: BlockDefinition} { const blocks: {[key: string]: BlockDefinition} = {}; for (let i = 0; i < jsonArray.length; i++) { @@ -279,7 +280,7 @@ export function createBlockDefinitionsFromJsonArray( console.warn(`Block definition #${i} in JSON array is ${elem}. Skipping`); continue; } - const type = elem['type']; + const type = elem.type; if (!type) { console.warn( `Block definition #${i} in JSON array is missing a type attribute. ` + diff --git a/core/interfaces/i_json_block_definition.ts b/core/interfaces/i_json_block_definition.ts new file mode 100644 index 00000000000..e256ee37bbe --- /dev/null +++ b/core/interfaces/i_json_block_definition.ts @@ -0,0 +1,97 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface JsonBlockDefinition { + type?: string; + style?: string; + colour?: string | number; + output: string | string[] | null; + previousStatement?: string | string[] | null; + nextStatement?: string | string[] | null; + outputShape?: number; + inputsInline?: boolean; + tooltip?: string; + helpUrl?: string; + extensions?: string[]; + mutator?: string; + enableContextMenu?: boolean; + suppressPrefixSuffix?: boolean; + + [key: `message${number}`]: string | undefined; + [key: `args${number}`]: BlockArg[] | undefined; + [key: `implicitAlign${number}`]: string | undefined; +} + +/** Block Arg */ +export type BlockArg = + | InputValueArg + | InputStatementArg + | InputDummyArg + | FieldInputArg + | FieldNumberArg + | FieldDropdownArg + | FieldCheckboxArg + | FieldImageArg + | FieldVariableArg; + +/** Common Arg */ +interface CommonArg { + name?: string; +} + +/** Input Args */ +interface InputValueArg extends CommonArg { + type: 'input_value'; + check?: string | string[]; + align?: FieldsAlign +} +interface InputStatementArg extends CommonArg { + type: 'input_statement'; + check?: string | string[]; +} +interface InputDummyArg extends CommonArg { + type: 'input_dummy'; +} + +/** Field Args */ +interface FieldInputArg extends CommonArg{ + type: 'field_input' + text: string +} + +interface FieldNumberArg extends CommonArg { + type: 'field_number'; + value?: number; + min?: number; + max?: number; + precision?: number; +} + +interface FieldDropdownArg extends CommonArg { + type: 'field_dropdown'; + options: [string, string][]; +} + +interface FieldCheckboxArg extends CommonArg { + type: 'field_checkbox'; + checked?: boolean | 'TRUE' | 'FALSE'; +} + +interface FieldImageArg { + type: 'field_image'; + src: string; + width: number; + height: number; + alt?: string; + flipRtl?: boolean | 'TRUE' | 'FALSE'; +} + +interface FieldVariableArg extends CommonArg { + type: 'field_variable' + variable: string | null +} + +export type FieldsAlign = 'LEFT' | 'RIGHT' | 'CENTRE' From 568baaaab1a121a1990026ab002c951b7b19939b Mon Sep 17 00:00:00 2001 From: heliacer Date: Fri, 3 Oct 2025 23:52:11 +0200 Subject: [PATCH 03/12] fix: revert accidental comment selector change not meant for this PR --- core/renderers/zelos/constants.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/renderers/zelos/constants.ts b/core/renderers/zelos/constants.ts index e035b6681a9..8cd36e02589 100644 --- a/core/renderers/zelos/constants.ts +++ b/core/renderers/zelos/constants.ts @@ -825,8 +825,7 @@ export class ConstantProvider extends BaseConstantProvider { `pt ${this.FIELD_TEXT_FONTFAMILY};`, `}`, - // Comments. - `${selector} .blocklyCommentText {`, + `${selector} .blocklyTextInputBubble textarea {`, `font-weight: normal;`, `}`, From d26a001564e2cedf393b47757c8d8057119f7b14 Mon Sep 17 00:00:00 2001 From: heliacer Date: Thu, 20 Nov 2025 19:14:39 +0100 Subject: [PATCH 04/12] use existing config --- core/interfaces/i_json_block_definition.ts | 106 ++++++++++----------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/core/interfaces/i_json_block_definition.ts b/core/interfaces/i_json_block_definition.ts index e256ee37bbe..5b6b6285c25 100644 --- a/core/interfaces/i_json_block_definition.ts +++ b/core/interfaces/i_json_block_definition.ts @@ -4,25 +4,32 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { FieldCheckboxFromJsonConfig } from "../field_checkbox" +import { FieldDropdownFromJsonConfig } from "../field_dropdown" +import { FieldImageFromJsonConfig } from "../field_image" +import { FieldNumberFromJsonConfig } from "../field_number" +import { FieldTextInputFromJsonConfig } from "../field_textinput" +import { FieldVariableFromJsonConfig } from "../field_variable" + export interface JsonBlockDefinition { - type?: string; - style?: string; - colour?: string | number; - output: string | string[] | null; - previousStatement?: string | string[] | null; - nextStatement?: string | string[] | null; - outputShape?: number; - inputsInline?: boolean; - tooltip?: string; - helpUrl?: string; - extensions?: string[]; - mutator?: string; - enableContextMenu?: boolean; - suppressPrefixSuffix?: boolean; + type?: string + style?: string + colour?: string | number + output?: string | string[] | null + previousStatement?: string | string[] | null + nextStatement?: string | string[] | null + outputShape?: number + inputsInline?: boolean + tooltip?: string + helpUrl?: string + extensions?: string[] + mutator?: string + enableContextMenu?: boolean + suppressPrefixSuffix?: boolean - [key: `message${number}`]: string | undefined; - [key: `args${number}`]: BlockArg[] | undefined; - [key: `implicitAlign${number}`]: string | undefined; + [key: `message${number}`]: string | undefined + [key: `args${number}`]: BlockArg[] | undefined + [key: `implicitAlign${number}`]: string | undefined } /** Block Arg */ @@ -35,63 +42,54 @@ export type BlockArg = | FieldDropdownArg | FieldCheckboxArg | FieldImageArg - | FieldVariableArg; - -/** Common Arg */ -interface CommonArg { - name?: string; -} + | FieldVariableArg /** Input Args */ -interface InputValueArg extends CommonArg { - type: 'input_value'; - check?: string | string[]; +interface InputValueArg { + name: string + type: 'input_value' + check?: string | string[] align?: FieldsAlign } -interface InputStatementArg extends CommonArg { - type: 'input_statement'; - check?: string | string[]; +interface InputStatementArg { + name: string + type: 'input_statement' + check?: string | string[] } -interface InputDummyArg extends CommonArg { - type: 'input_dummy'; +interface InputDummyArg { + name: string + type: 'input_dummy' } /** Field Args */ -interface FieldInputArg extends CommonArg{ +interface FieldInputArg extends FieldTextInputFromJsonConfig { + name: string type: 'field_input' - text: string } -interface FieldNumberArg extends CommonArg { - type: 'field_number'; - value?: number; - min?: number; - max?: number; - precision?: number; +interface FieldNumberArg extends FieldNumberFromJsonConfig { + name: string + type: 'field_number' } -interface FieldDropdownArg extends CommonArg { - type: 'field_dropdown'; - options: [string, string][]; +interface FieldDropdownArg extends FieldDropdownFromJsonConfig { + name: string + type: 'field_dropdown' } -interface FieldCheckboxArg extends CommonArg { - type: 'field_checkbox'; - checked?: boolean | 'TRUE' | 'FALSE'; +interface FieldCheckboxArg extends FieldCheckboxFromJsonConfig { + name: string + type: 'field_checkbox' } -interface FieldImageArg { - type: 'field_image'; - src: string; - width: number; - height: number; - alt?: string; - flipRtl?: boolean | 'TRUE' | 'FALSE'; +interface FieldImageArg extends FieldImageFromJsonConfig { + name: string + type: 'field_image' } -interface FieldVariableArg extends CommonArg { +interface FieldVariableArg extends FieldVariableFromJsonConfig { + name: string type: 'field_variable' - variable: string | null } export type FieldsAlign = 'LEFT' | 'RIGHT' | 'CENTRE' From f0868e8aabb3a9b78289382d7b0b7aa9e1501a9f Mon Sep 17 00:00:00 2001 From: heliacer Date: Wed, 17 Dec 2025 15:16:39 +0100 Subject: [PATCH 05/12] remove enforced output: null, make name optional for input dummies --- blocks/logic.ts | 7 ------- blocks/loops.ts | 8 -------- blocks/math.ts | 1 - blocks/text.ts | 4 ---- blocks/variables.ts | 2 -- blocks/variables_dynamic.ts | 2 -- core/interfaces/i_json_block_definition.ts | 2 +- 7 files changed, 1 insertion(+), 25 deletions(-) diff --git a/blocks/logic.ts b/blocks/logic.ts index 6cbcd83a1f9..9cbcff4729c 100644 --- a/blocks/logic.ts +++ b/blocks/logic.ts @@ -64,7 +64,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'DO0', }, ], - 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'logic_blocks', @@ -98,7 +97,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'ELSE', }, ], - 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'logic_blocks', @@ -189,7 +187,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'logic_null', 'message0': '%{BKY_LOGIC_NULL}', - 'output': null, 'style': 'logic_blocks', 'tooltip': '%{BKY_LOGIC_NULL_TOOLTIP}', 'helpUrl': '%{BKY_LOGIC_NULL_HELPURL}', @@ -219,7 +216,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'ELSE', }, ], - 'output': null, 'style': 'logic_blocks', 'tooltip': '%{BKY_LOGIC_TERNARY_TOOLTIP}', 'helpUrl': '%{BKY_LOGIC_TERNARY_HELPURL}', @@ -230,7 +226,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'type': 'controls_if_if', 'message0': '%{BKY_CONTROLS_IF_IF_TITLE_IF}', 'nextStatement': null, - 'output': null, 'enableContextMenu': false, 'style': 'logic_blocks', 'tooltip': '%{BKY_CONTROLS_IF_IF_TOOLTIP}', @@ -241,7 +236,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'message0': '%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}', 'previousStatement': null, 'nextStatement': null, - 'output': null, 'enableContextMenu': false, 'style': 'logic_blocks', 'tooltip': '%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}', @@ -251,7 +245,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'type': 'controls_if_else', 'message0': '%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}', 'previousStatement': null, - 'output': null, 'enableContextMenu': false, 'style': 'logic_blocks', 'tooltip': '%{BKY_CONTROLS_IF_ELSE_TOOLTIP}', diff --git a/blocks/loops.ts b/blocks/loops.ts index a79b2ff8a45..3eac94fa71d 100644 --- a/blocks/loops.ts +++ b/blocks/loops.ts @@ -53,7 +53,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'loop_blocks', 'tooltip': '%{BKY_CONTROLS_REPEAT_TOOLTIP}', 'helpUrl': '%{BKY_CONTROLS_REPEAT_HELPURL}', @@ -81,7 +80,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'loop_blocks', 'tooltip': '%{BKY_CONTROLS_REPEAT_TOOLTIP}', 'helpUrl': '%{BKY_CONTROLS_REPEAT_HELPURL}', @@ -114,7 +112,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_WHILEUNTIL_HELPURL}', 'extensions': ['controls_whileUntil_tooltip'], @@ -127,7 +124,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'field_variable', 'name': 'VAR', - 'variable': null, }, { 'type': 'input_value', @@ -158,7 +154,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'inputsInline': true, 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_FOR_HELPURL}', 'extensions': ['contextMenu_newGetVariableBlock', 'controls_for_tooltip'], @@ -171,7 +166,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'field_variable', 'name': 'VAR', - 'variable': null, }, { 'type': 'input_value', @@ -188,7 +182,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_FOREACH_HELPURL}', 'extensions': [ @@ -211,7 +204,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ }, ], 'previousStatement': null, - 'output': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}', 'suppressPrefixSuffix': true, diff --git a/blocks/math.ts b/blocks/math.ts index 238148e0689..e5aef5fbb6e 100644 --- a/blocks/math.ts +++ b/blocks/math.ts @@ -208,7 +208,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'variable_blocks', 'helpUrl': '%{BKY_MATH_CHANGE_HELPURL}', 'extensions': ['math_change_tooltip'], diff --git a/blocks/text.ts b/blocks/text.ts index 458a9bab7b2..a7ad5374ac4 100644 --- a/blocks/text.ts +++ b/blocks/text.ts @@ -68,7 +68,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'STACK', }, ], - 'output': null, 'style': 'text_blocks', 'tooltip': '%{BKY_TEXT_CREATE_JOIN_TOOLTIP}', 'enableContextMenu': false, @@ -79,7 +78,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'previousStatement': null, 'nextStatement': null, 'style': 'text_blocks', - 'output': null, 'tooltip': '%{BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}', 'enableContextMenu': false, }, @@ -99,7 +97,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'text_blocks', 'extensions': ['text_append_tooltip'], }, @@ -390,7 +387,6 @@ blocks['text_print'] = { 'name': 'TEXT', }, ], - 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'text_blocks', diff --git a/blocks/variables.ts b/blocks/variables.ts index e2bf96574f3..d7d5e09872d 100644 --- a/blocks/variables.ts +++ b/blocks/variables.ts @@ -37,7 +37,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', }, ], - 'output': null, 'style': 'variable_blocks', 'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}', 'tooltip': '%{BKY_VARIABLES_GET_TOOLTIP}', @@ -60,7 +59,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'variable_blocks', 'tooltip': '%{BKY_VARIABLES_SET_TOOLTIP}', 'helpUrl': '%{BKY_VARIABLES_SET_HELPURL}', diff --git a/blocks/variables_dynamic.ts b/blocks/variables_dynamic.ts index d2944115d05..18f90d1b671 100644 --- a/blocks/variables_dynamic.ts +++ b/blocks/variables_dynamic.ts @@ -38,7 +38,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', }, ], - 'output': null, 'style': 'variable_dynamic_blocks', 'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}', 'tooltip': '%{BKY_VARIABLES_GET_TOOLTIP}', @@ -59,7 +58,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'VALUE', }, ], - 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'variable_dynamic_blocks', diff --git a/core/interfaces/i_json_block_definition.ts b/core/interfaces/i_json_block_definition.ts index 5b6b6285c25..82385a94c8e 100644 --- a/core/interfaces/i_json_block_definition.ts +++ b/core/interfaces/i_json_block_definition.ts @@ -57,7 +57,7 @@ interface InputStatementArg { check?: string | string[] } interface InputDummyArg { - name: string + name?: string type: 'input_dummy' } From 8c62221d686a5b879baf30cae56965b535627752 Mon Sep 17 00:00:00 2001 From: heliacer Date: Wed, 17 Dec 2025 15:40:52 +0100 Subject: [PATCH 06/12] fix: re-add output (again) since some were necessary after all --- blocks/logic.ts | 302 ++++++++++++++++++------------------ blocks/variables.ts | 86 +++++----- blocks/variables_dynamic.ts | 95 ++++++------ 3 files changed, 244 insertions(+), 239 deletions(-) diff --git a/blocks/logic.ts b/blocks/logic.ts index 9cbcff4729c..89dbf631c43 100644 --- a/blocks/logic.ts +++ b/blocks/logic.ts @@ -6,22 +6,22 @@ // Former goog.module ID: Blockly.libraryBlocks.logic -import type {Block} from '../core/block.js'; -import type {BlockSvg} from '../core/block_svg.js'; +import type { Block } from '../core/block.js' +import type { BlockSvg } from '../core/block_svg.js' import { createBlockDefinitionsFromJsonArray, defineBlocks, -} from '../core/common.js'; -import type {Connection} from '../core/connection.js'; -import * as Events from '../core/events/events.js'; -import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js'; -import * as Extensions from '../core/extensions.js'; -import '../core/field_dropdown.js'; -import '../core/field_label.js'; -import '../core/icons/mutator_icon.js'; -import {Msg} from '../core/msg.js'; -import * as xmlUtils from '../core/utils/xml.js'; -import type {Workspace} from '../core/workspace.js'; +} from '../core/common.js' +import type { Connection } from '../core/connection.js' +import * as Events from '../core/events/events.js' +import type { Abstract as AbstractEvent } from '../core/events/events_abstract.js' +import * as Extensions from '../core/extensions.js' +import '../core/field_dropdown.js' +import '../core/field_label.js' +import '../core/icons/mutator_icon.js' +import { Msg } from '../core/msg.js' +import * as xmlUtils from '../core/utils/xml.js' +import type { Workspace } from '../core/workspace.js' /** * A dictionary of the block definitions provided by this module. @@ -187,6 +187,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'logic_null', 'message0': '%{BKY_LOGIC_NULL}', + 'output': null, 'style': 'logic_blocks', 'tooltip': '%{BKY_LOGIC_NULL_TOOLTIP}', 'helpUrl': '%{BKY_LOGIC_NULL_HELPURL}', @@ -216,6 +217,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'ELSE', }, ], + 'output': null, 'style': 'logic_blocks', 'tooltip': '%{BKY_LOGIC_TERNARY_TOOLTIP}', 'helpUrl': '%{BKY_LOGIC_TERNARY_HELPURL}', @@ -249,7 +251,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'style': 'logic_blocks', 'tooltip': '%{BKY_CONTROLS_IF_ELSE_TOOLTIP}', }, -]); +]) /** * Tooltip text, keyed by block OP value. Used by logic_compare and @@ -269,33 +271,33 @@ const TOOLTIPS_BY_OP = { // logic_operation 'AND': '%{BKY_LOGIC_OPERATION_TOOLTIP_AND}', 'OR': '%{BKY_LOGIC_OPERATION_TOOLTIP_OR}', -}; +} Extensions.register( 'logic_op_tooltip', Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP), -); +) /** Type of a block that has CONTROLS_IF_MUTATOR_MIXIN */ -type IfBlock = Block & IfMixin; -interface IfMixin extends IfMixinType {} -type IfMixinType = typeof CONTROLS_IF_MUTATOR_MIXIN; +type IfBlock = Block & IfMixin +interface IfMixin extends IfMixinType { } +type IfMixinType = typeof CONTROLS_IF_MUTATOR_MIXIN // Types for quarks defined in JSON. /** Type of a controls_if_if (if mutator container) block. */ -interface ContainerBlock extends Block {} +interface ContainerBlock extends Block { } /** Type of a controls_if_elseif or controls_if_else block. */ interface ClauseBlock extends Block { - valueConnection_?: Connection | null; - statementConnection_?: Connection | null; + valueConnection_?: Connection | null + statementConnection_?: Connection | null } /** Extra state for serialising controls_if blocks. */ type IfExtraState = { - elseIfCount?: number; - hasElse?: boolean; -}; + elseIfCount?: number + hasElse?: boolean +} /** * Mutator methods added to controls_if blocks. @@ -312,16 +314,16 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ mutationToDom: function (this: IfBlock): Element | null { if (!this.elseifCount_ && !this.elseCount_) { - return null; + return null } - const container = xmlUtils.createElement('mutation'); + const container = xmlUtils.createElement('mutation') if (this.elseifCount_) { - container.setAttribute('elseif', String(this.elseifCount_)); + container.setAttribute('elseif', String(this.elseifCount_)) } if (this.elseCount_) { - container.setAttribute('else', '1'); + container.setAttribute('else', '1') } - return container; + return container }, /** * Parse XML to restore the else-if and else inputs. @@ -330,9 +332,9 @@ const CONTROLS_IF_MUTATOR_MIXIN = { * @param xmlElement XML storage element. */ domToMutation: function (this: IfBlock, xmlElement: Element) { - this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif')!, 10) || 0; - this.elseCount_ = parseInt(xmlElement.getAttribute('else')!, 10) || 0; - this.rebuildShape_(); + this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif')!, 10) || 0 + this.elseCount_ = parseInt(xmlElement.getAttribute('else')!, 10) || 0 + this.rebuildShape_() }, /** * Returns the state of this block as a JSON serializable object. @@ -341,16 +343,16 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ saveExtraState: function (this: IfBlock): IfExtraState | null { if (!this.elseifCount_ && !this.elseCount_) { - return null; + return null } - const state = Object.create(null); + const state = Object.create(null) if (this.elseifCount_) { - state['elseIfCount'] = this.elseifCount_; + state['elseIfCount'] = this.elseifCount_ } if (this.elseCount_) { - state['hasElse'] = true; + state['hasElse'] = true } - return state; + return state }, /** * Applies the given state to this block. @@ -360,9 +362,9 @@ const CONTROLS_IF_MUTATOR_MIXIN = { * else state. */ loadExtraState: function (this: IfBlock, state: IfExtraState) { - this.elseifCount_ = state['elseIfCount'] || 0; - this.elseCount_ = state['hasElse'] ? 1 : 0; - this.updateShape_(); + this.elseifCount_ = state['elseIfCount'] || 0 + this.elseCount_ = state['hasElse'] ? 1 : 0 + this.updateShape_() }, /** * Populate the mutator's dialog with this block's components. @@ -372,20 +374,20 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ decompose: function (this: IfBlock, workspace: Workspace): ContainerBlock { const containerBlock = workspace.newBlock('controls_if_if'); - (containerBlock as BlockSvg).initSvg(); - let connection = containerBlock.nextConnection!; + (containerBlock as BlockSvg).initSvg() + let connection = containerBlock.nextConnection! for (let i = 1; i <= this.elseifCount_; i++) { const elseifBlock = workspace.newBlock('controls_if_elseif'); - (elseifBlock as BlockSvg).initSvg(); - connection.connect(elseifBlock.previousConnection!); - connection = elseifBlock.nextConnection!; + (elseifBlock as BlockSvg).initSvg() + connection.connect(elseifBlock.previousConnection!) + connection = elseifBlock.nextConnection! } if (this.elseCount_) { const elseBlock = workspace.newBlock('controls_if_else'); - (elseBlock as BlockSvg).initSvg(); - connection.connect(elseBlock.previousConnection!); + (elseBlock as BlockSvg).initSvg() + connection.connect(elseBlock.previousConnection!) } - return containerBlock; + return containerBlock }, /** * Reconfigure this block based on the mutator dialog's components. @@ -394,49 +396,49 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ compose: function (this: IfBlock, containerBlock: ContainerBlock) { let clauseBlock = - containerBlock.nextConnection!.targetBlock() as ClauseBlock | null; + containerBlock.nextConnection!.targetBlock() as ClauseBlock | null // Count number of inputs. - this.elseifCount_ = 0; - this.elseCount_ = 0; + this.elseifCount_ = 0 + this.elseCount_ = 0 // Connections arrays are passed to .reconnectChildBlocks_() which // takes 1-based arrays, so are initialised with a dummy value at // index 0 for convenience. - const valueConnections: Array = [null]; - const statementConnections: Array = [null]; - let elseStatementConnection: Connection | null = null; + const valueConnections: Array = [null] + const statementConnections: Array = [null] + let elseStatementConnection: Connection | null = null while (clauseBlock) { if (clauseBlock.isInsertionMarker()) { - clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null; - continue; + clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null + continue } switch (clauseBlock.type) { case 'controls_if_elseif': - this.elseifCount_++; + this.elseifCount_++ // TODO(#6920): null valid, undefined not. valueConnections.push( clauseBlock.valueConnection_ as Connection | null, - ); + ) statementConnections.push( clauseBlock.statementConnection_ as Connection | null, - ); - break; + ) + break case 'controls_if_else': - this.elseCount_++; + this.elseCount_++ elseStatementConnection = - clauseBlock.statementConnection_ as Connection | null; - break; + clauseBlock.statementConnection_ as Connection | null + break default: - throw TypeError('Unknown block type: ' + clauseBlock.type); + throw TypeError('Unknown block type: ' + clauseBlock.type) } - clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null; + clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null } - this.updateShape_(); + this.updateShape_() // Reconnect any child blocks. this.reconnectChildBlocks_( valueConnections, statementConnections, elseStatementConnection, - ); + ) }, /** * Store pointers to any connected child blocks. @@ -445,60 +447,60 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ saveConnections: function (this: IfBlock, containerBlock: ContainerBlock) { let clauseBlock = - containerBlock!.nextConnection!.targetBlock() as ClauseBlock | null; - let i = 1; + containerBlock!.nextConnection!.targetBlock() as ClauseBlock | null + let i = 1 while (clauseBlock) { if (clauseBlock.isInsertionMarker()) { - clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null; - continue; + clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null + continue } switch (clauseBlock.type) { case 'controls_if_elseif': { - const inputIf = this.getInput('IF' + i); - const inputDo = this.getInput('DO' + i); + const inputIf = this.getInput('IF' + i) + const inputDo = this.getInput('DO' + i) clauseBlock.valueConnection_ = - inputIf && inputIf.connection!.targetConnection; + inputIf && inputIf.connection!.targetConnection clauseBlock.statementConnection_ = - inputDo && inputDo.connection!.targetConnection; - i++; - break; + inputDo && inputDo.connection!.targetConnection + i++ + break } case 'controls_if_else': { - const inputDo = this.getInput('ELSE'); + const inputDo = this.getInput('ELSE') clauseBlock.statementConnection_ = - inputDo && inputDo.connection!.targetConnection; - break; + inputDo && inputDo.connection!.targetConnection + break } default: - throw TypeError('Unknown block type: ' + clauseBlock.type); + throw TypeError('Unknown block type: ' + clauseBlock.type) } - clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null; + clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null } }, /** * Reconstructs the block with all child blocks attached. */ rebuildShape_: function (this: IfBlock) { - const valueConnections: Array = [null]; - const statementConnections: Array = [null]; - let elseStatementConnection: Connection | null = null; + const valueConnections: Array = [null] + const statementConnections: Array = [null] + let elseStatementConnection: Connection | null = null if (this.getInput('ELSE')) { elseStatementConnection = - this.getInput('ELSE')!.connection!.targetConnection; + this.getInput('ELSE')!.connection!.targetConnection } for (let i = 1; this.getInput('IF' + i); i++) { - const inputIf = this.getInput('IF' + i); - const inputDo = this.getInput('DO' + i); - valueConnections.push(inputIf!.connection!.targetConnection); - statementConnections.push(inputDo!.connection!.targetConnection); + const inputIf = this.getInput('IF' + i) + const inputDo = this.getInput('DO' + i) + valueConnections.push(inputIf!.connection!.targetConnection) + statementConnections.push(inputDo!.connection!.targetConnection) } - this.updateShape_(); + this.updateShape_() this.reconnectChildBlocks_( valueConnections, statementConnections, elseStatementConnection, - ); + ) }, /** * Modify this block to have the correct number of inputs. @@ -508,25 +510,25 @@ const CONTROLS_IF_MUTATOR_MIXIN = { updateShape_: function (this: IfBlock) { // Delete everything. if (this.getInput('ELSE')) { - this.removeInput('ELSE'); + this.removeInput('ELSE') } for (let i = 1; this.getInput('IF' + i); i++) { - this.removeInput('IF' + i); - this.removeInput('DO' + i); + this.removeInput('IF' + i) + this.removeInput('DO' + i) } // Rebuild block. for (let i = 1; i <= this.elseifCount_; i++) { this.appendValueInput('IF' + i) .setCheck('Boolean') - .appendField(Msg['CONTROLS_IF_MSG_ELSEIF']); + .appendField(Msg['CONTROLS_IF_MSG_ELSEIF']) this.appendStatementInput('DO' + i).appendField( Msg['CONTROLS_IF_MSG_THEN'], - ); + ) } if (this.elseCount_) { this.appendStatementInput('ELSE').appendField( Msg['CONTROLS_IF_MSG_ELSE'], - ); + ) } }, /** @@ -545,19 +547,19 @@ const CONTROLS_IF_MUTATOR_MIXIN = { elseStatementConnection: Connection | null, ) { for (let i = 1; i <= this.elseifCount_; i++) { - valueConnections[i]?.reconnect(this, 'IF' + i); - statementConnections[i]?.reconnect(this, 'DO' + i); + valueConnections[i]?.reconnect(this, 'IF' + i) + statementConnections[i]?.reconnect(this, 'DO' + i) } - elseStatementConnection?.reconnect(this, 'ELSE'); + elseStatementConnection?.reconnect(this, 'ELSE') }, -}; +} Extensions.registerMutator( 'controls_if_mutator', CONTROLS_IF_MUTATOR_MIXIN, null as unknown as undefined, // TODO(#6920) ['controls_if_elseif', 'controls_if_else'], -); +) /** * "controls_if" extension function. Adds mutator, shape updating methods, @@ -567,27 +569,27 @@ const CONTROLS_IF_TOOLTIP_EXTENSION = function (this: IfBlock) { this.setTooltip( function (this: IfBlock) { if (!this.elseifCount_ && !this.elseCount_) { - return Msg['CONTROLS_IF_TOOLTIP_1']; + return Msg['CONTROLS_IF_TOOLTIP_1'] } else if (!this.elseifCount_ && this.elseCount_) { - return Msg['CONTROLS_IF_TOOLTIP_2']; + return Msg['CONTROLS_IF_TOOLTIP_2'] } else if (this.elseifCount_ && !this.elseCount_) { - return Msg['CONTROLS_IF_TOOLTIP_3']; + return Msg['CONTROLS_IF_TOOLTIP_3'] } else if (this.elseifCount_ && this.elseCount_) { - return Msg['CONTROLS_IF_TOOLTIP_4']; + return Msg['CONTROLS_IF_TOOLTIP_4'] } - return ''; + return '' }.bind(this), - ); -}; + ) +} -Extensions.register('controls_if_tooltip', CONTROLS_IF_TOOLTIP_EXTENSION); +Extensions.register('controls_if_tooltip', CONTROLS_IF_TOOLTIP_EXTENSION) /** Type of a block that has LOGIC_COMPARE_ONCHANGE_MIXIN */ -type CompareBlock = Block & CompareMixin; +type CompareBlock = Block & CompareMixin interface CompareMixin extends CompareMixinType { - prevBlocks_?: Array; + prevBlocks_?: Array } -type CompareMixinType = typeof LOGIC_COMPARE_ONCHANGE_MIXIN; +type CompareMixinType = typeof LOGIC_COMPARE_ONCHANGE_MIXIN /** * Adds dynamic type validation for the left and right sides of a @@ -602,11 +604,11 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = { */ onchange: function (this: CompareBlock, e: AbstractEvent) { if (!this.prevBlocks_) { - this.prevBlocks_ = [null, null]; + this.prevBlocks_ = [null, null] } - const blockA = this.getInputTargetBlock('A'); - const blockB = this.getInputTargetBlock('B'); + const blockA = this.getInputTargetBlock('A') + const blockB = this.getInputTargetBlock('B') // Disconnect blocks that existed prior to this change if they don't // match. if ( @@ -619,30 +621,30 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = { ) { // Mismatch between two inputs. Revert the block connections, // bumping away the newly connected block(s). - Events.setGroup(e.group); - const prevA = this.prevBlocks_[0]; + Events.setGroup(e.group) + const prevA = this.prevBlocks_[0] if (prevA !== blockA) { - blockA.unplug(); + blockA.unplug() if (prevA && !prevA.isDisposed() && !prevA.isShadow()) { // The shadow block is automatically replaced during unplug(). - this.getInput('A')!.connection!.connect(prevA.outputConnection!); + this.getInput('A')!.connection!.connect(prevA.outputConnection!) } } - const prevB = this.prevBlocks_[1]; + const prevB = this.prevBlocks_[1] if (prevB !== blockB) { - blockB.unplug(); + blockB.unplug() if (prevB && !prevB.isDisposed() && !prevB.isShadow()) { // The shadow block is automatically replaced during unplug(). - this.getInput('B')!.connection!.connect(prevB.outputConnection!); + this.getInput('B')!.connection!.connect(prevB.outputConnection!) } } - this.bumpNeighbours(); - Events.setGroup(false); + this.bumpNeighbours() + Events.setGroup(false) } - this.prevBlocks_[0] = this.getInputTargetBlock('A'); - this.prevBlocks_[1] = this.getInputTargetBlock('B'); + this.prevBlocks_[0] = this.getInputTargetBlock('A') + this.prevBlocks_[1] = this.getInputTargetBlock('B') }, -}; +} /** * "logic_compare" extension function. Adds type left and right side type @@ -650,15 +652,15 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = { */ const LOGIC_COMPARE_EXTENSION = function (this: CompareBlock) { // Add onchange handler to ensure types are compatible. - this.mixin(LOGIC_COMPARE_ONCHANGE_MIXIN); -}; + this.mixin(LOGIC_COMPARE_ONCHANGE_MIXIN) +} -Extensions.register('logic_compare', LOGIC_COMPARE_EXTENSION); +Extensions.register('logic_compare', LOGIC_COMPARE_EXTENSION) /** Type of a block that has LOGIC_TERNARY_ONCHANGE_MIXIN */ -type TernaryBlock = Block & TernaryMixin; -interface TernaryMixin extends TernaryMixinType {} -type TernaryMixinType = typeof LOGIC_TERNARY_ONCHANGE_MIXIN; +type TernaryBlock = Block & TernaryMixin +interface TernaryMixin extends TernaryMixinType { } +type TernaryMixinType = typeof LOGIC_TERNARY_ONCHANGE_MIXIN /** * Adds type coordination between inputs and output. @@ -671,14 +673,14 @@ const LOGIC_TERNARY_ONCHANGE_MIXIN = { * Prevent mismatched types. */ onchange: function (this: TernaryBlock, e: AbstractEvent) { - const blockA = this.getInputTargetBlock('THEN'); - const blockB = this.getInputTargetBlock('ELSE'); - const parentConnection = this.outputConnection!.targetConnection; + const blockA = this.getInputTargetBlock('THEN') + const blockB = this.getInputTargetBlock('ELSE') + const parentConnection = this.outputConnection!.targetConnection // Disconnect blocks that existed prior to this change if they don't // match. if ((blockA || blockB) && parentConnection) { for (let i = 0; i < 2; i++) { - const block = i === 1 ? blockA : blockB; + const block = i === 1 ? blockA : blockB if ( block && !block.workspace.connectionChecker.doTypeChecks( @@ -688,23 +690,23 @@ const LOGIC_TERNARY_ONCHANGE_MIXIN = { ) { // Ensure that any disconnections are grouped with the causing // event. - Events.setGroup(e.group); + Events.setGroup(e.group) if (parentConnection === this.prevParentConnection_) { - this.unplug(); - parentConnection.getSourceBlock().bumpNeighbours(); + this.unplug() + parentConnection.getSourceBlock().bumpNeighbours() } else { - block.unplug(); - block.bumpNeighbours(); + block.unplug() + block.bumpNeighbours() } - Events.setGroup(false); + Events.setGroup(false) } } } - this.prevParentConnection_ = parentConnection; + this.prevParentConnection_ = parentConnection }, -}; +} -Extensions.registerMixin('logic_ternary', LOGIC_TERNARY_ONCHANGE_MIXIN); +Extensions.registerMixin('logic_ternary', LOGIC_TERNARY_ONCHANGE_MIXIN) // Register provided blocks. -defineBlocks(blocks); +defineBlocks(blocks) diff --git a/blocks/variables.ts b/blocks/variables.ts index d7d5e09872d..725376165fa 100644 --- a/blocks/variables.ts +++ b/blocks/variables.ts @@ -6,21 +6,21 @@ // Former goog.module ID: Blockly.libraryBlocks.variables -import type {Block} from '../core/block.js'; +import type { Block } from '../core/block.js' import { createBlockDefinitionsFromJsonArray, defineBlocks, -} from '../core/common.js'; -import * as ContextMenu from '../core/contextmenu.js'; +} from '../core/common.js' +import * as ContextMenu from '../core/contextmenu.js' import type { ContextMenuOption, LegacyContextMenuOption, -} from '../core/contextmenu_registry.js'; -import * as Extensions from '../core/extensions.js'; -import '../core/field_label.js'; -import {FieldVariable} from '../core/field_variable.js'; -import {Msg} from '../core/msg.js'; -import * as Variables from '../core/variables.js'; +} from '../core/contextmenu_registry.js' +import * as Extensions from '../core/extensions.js' +import '../core/field_label.js' +import { FieldVariable } from '../core/field_variable.js' +import { Msg } from '../core/msg.js' +import * as Variables from '../core/variables.js' /** * A dictionary of the block definitions provided by this module. @@ -37,6 +37,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', }, ], + 'output': null, 'style': 'variable_blocks', 'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}', 'tooltip': '%{BKY_VARIABLES_GET_TOOLTIP}', @@ -59,18 +60,19 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, + 'output': null, 'style': 'variable_blocks', 'tooltip': '%{BKY_VARIABLES_SET_TOOLTIP}', 'helpUrl': '%{BKY_VARIABLES_SET_HELPURL}', 'extensions': ['contextMenu_variableSetterGetter'], }, -]); +]) /** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */ -type VariableBlock = Block & VariableMixin; -interface VariableMixin extends VariableMixinType {} +type VariableBlock = Block & VariableMixin +interface VariableMixin extends VariableMixinType { } type VariableMixinType = - typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN; + typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN /** * Mixin to add context menu items to create getter/setter blocks for this @@ -88,28 +90,28 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { options: Array, ) { if (!this.isInFlyout) { - let oppositeType; - let contextMenuMsg; + let oppositeType + let contextMenuMsg // Getter blocks have the option to create a setter block, and vice versa. if (this.type === 'variables_get') { - oppositeType = 'variables_set'; - contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET']; + oppositeType = 'variables_set' + contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET'] } else { - oppositeType = 'variables_get'; - contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET']; + oppositeType = 'variables_get' + contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET'] } - const varField = this.getField('VAR')!; + const varField = this.getField('VAR')! const newVarBlockState = { type: oppositeType, - fields: {VAR: varField.saveState(true)}, - }; + fields: { VAR: varField.saveState(true) }, + } options.push({ enabled: this.workspace.remainingCapacity() > 0, text: contextMenuMsg.replace('%1', varField.getText()), callback: ContextMenu.callbackFactory(this, newVarBlockState), - }); + }) // Getter blocks have the option to rename or delete that variable. } else { if ( @@ -120,19 +122,19 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { text: Msg['RENAME_VARIABLE'], enabled: true, callback: renameOptionCallbackFactory(this), - }; - const name = this.getField('VAR')!.getText(); + } + const name = this.getField('VAR')!.getText() const deleteOption = { text: Msg['DELETE_VARIABLE'].replace('%1', name), enabled: true, callback: deleteOptionCallbackFactory(this), - }; - options.unshift(renameOption); - options.unshift(deleteOption); + } + options.unshift(renameOption) + options.unshift(deleteOption) } } }, -}; +} /** * Factory for callbacks for rename variable dropdown menu option @@ -145,12 +147,12 @@ const renameOptionCallbackFactory = function ( block: VariableBlock, ): () => void { return function () { - const workspace = block.workspace; - const variableField = block.getField('VAR') as FieldVariable; - const variable = variableField.getVariable()!; - Variables.renameVariable(workspace, variable); - }; -}; + const workspace = block.workspace + const variableField = block.getField('VAR') as FieldVariable + const variable = variableField.getVariable()! + Variables.renameVariable(workspace, variable) + } +} /** * Factory for callbacks for delete variable dropdown menu option @@ -163,18 +165,18 @@ const deleteOptionCallbackFactory = function ( block: VariableBlock, ): () => void { return function () { - const variableField = block.getField('VAR') as FieldVariable; - const variable = variableField.getVariable(); + const variableField = block.getField('VAR') as FieldVariable + const variable = variableField.getVariable() if (variable) { - Variables.deleteVariable(variable.getWorkspace(), variable, block); + Variables.deleteVariable(variable.getWorkspace(), variable, block) } - }; -}; + } +} Extensions.registerMixin( 'contextMenu_variableSetterGetter', CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN, -); +) // Register provided blocks. -defineBlocks(blocks); +defineBlocks(blocks) diff --git a/blocks/variables_dynamic.ts b/blocks/variables_dynamic.ts index 18f90d1b671..b4b23039fe2 100644 --- a/blocks/variables_dynamic.ts +++ b/blocks/variables_dynamic.ts @@ -6,22 +6,22 @@ // Former goog.module ID: Blockly.libraryBlocks.variablesDynamic -import type {Block} from '../core/block.js'; +import type { Block } from '../core/block.js' import { createBlockDefinitionsFromJsonArray, defineBlocks, -} from '../core/common.js'; -import * as ContextMenu from '../core/contextmenu.js'; +} from '../core/common.js' +import * as ContextMenu from '../core/contextmenu.js' import type { ContextMenuOption, LegacyContextMenuOption, -} from '../core/contextmenu_registry.js'; -import {Abstract as AbstractEvent} from '../core/events/events_abstract.js'; -import * as Extensions from '../core/extensions.js'; -import '../core/field_label.js'; -import {FieldVariable} from '../core/field_variable.js'; -import {Msg} from '../core/msg.js'; -import * as Variables from '../core/variables.js'; +} from '../core/contextmenu_registry.js' +import { Abstract as AbstractEvent } from '../core/events/events_abstract.js' +import * as Extensions from '../core/extensions.js' +import '../core/field_label.js' +import { FieldVariable } from '../core/field_variable.js' +import { Msg } from '../core/msg.js' +import * as Variables from '../core/variables.js' /** * A dictionary of the block definitions provided by this module. @@ -58,6 +58,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'name': 'VALUE', }, ], + 'output': null, 'previousStatement': null, 'nextStatement': null, 'style': 'variable_dynamic_blocks', @@ -65,13 +66,13 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'helpUrl': '%{BKY_VARIABLES_SET_HELPURL}', 'extensions': ['contextMenu_variableDynamicSetterGetter'], }, -]); +]) /** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */ -type VariableBlock = Block & VariableMixin; -interface VariableMixin extends VariableMixinType {} +type VariableBlock = Block & VariableMixin +interface VariableMixin extends VariableMixinType { } type VariableMixinType = - typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN; + typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN /** * Mixin to add context menu items to create getter/setter blocks for this @@ -90,27 +91,27 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { ) { // Getter blocks have the option to create a setter block, and vice versa. if (!this.isInFlyout) { - let oppositeType; - let contextMenuMsg; + let oppositeType + let contextMenuMsg if (this.type === 'variables_get_dynamic') { - oppositeType = 'variables_set_dynamic'; - contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET']; + oppositeType = 'variables_set_dynamic' + contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET'] } else { - oppositeType = 'variables_get_dynamic'; - contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET']; + oppositeType = 'variables_get_dynamic' + contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET'] } - const varField = this.getField('VAR')!; + const varField = this.getField('VAR')! const newVarBlockState = { type: oppositeType, - fields: {VAR: varField.saveState(true)}, - }; + fields: { VAR: varField.saveState(true) }, + } options.push({ enabled: this.workspace.remainingCapacity() > 0, text: contextMenuMsg.replace('%1', varField.getText()), callback: ContextMenu.callbackFactory(this, newVarBlockState), - }); + }) } else { if ( this.type === 'variables_get_dynamic' || @@ -120,15 +121,15 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { text: Msg['RENAME_VARIABLE'], enabled: true, callback: renameOptionCallbackFactory(this), - }; - const name = this.getField('VAR')!.getText(); + } + const name = this.getField('VAR')!.getText() const deleteOption = { text: Msg['DELETE_VARIABLE'].replace('%1', name), enabled: true, callback: deleteOptionCallbackFactory(this), - }; - options.unshift(renameOption); - options.unshift(deleteOption); + } + options.unshift(renameOption) + options.unshift(deleteOption) } } }, @@ -139,15 +140,15 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { * @param _e Change event. */ onchange: function (this: VariableBlock, _e: AbstractEvent) { - const id = this.getFieldValue('VAR'); - const variableModel = Variables.getVariable(this.workspace, id)!; + const id = this.getFieldValue('VAR') + const variableModel = Variables.getVariable(this.workspace, id)! if (this.type === 'variables_get_dynamic') { - this.outputConnection!.setCheck(variableModel.getType()); + this.outputConnection!.setCheck(variableModel.getType()) } else { - this.getInput('VALUE')!.connection!.setCheck(variableModel.getType()); + this.getInput('VALUE')!.connection!.setCheck(variableModel.getType()) } }, -}; +} /** * Factory for callbacks for rename variable dropdown menu option @@ -158,12 +159,12 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { */ const renameOptionCallbackFactory = function (block: VariableBlock) { return function () { - const workspace = block.workspace; - const variableField = block.getField('VAR') as FieldVariable; - const variable = variableField.getVariable()!; - Variables.renameVariable(workspace, variable); - }; -}; + const workspace = block.workspace + const variableField = block.getField('VAR') as FieldVariable + const variable = variableField.getVariable()! + Variables.renameVariable(workspace, variable) + } +} /** * Factory for callbacks for delete variable dropdown menu option @@ -174,18 +175,18 @@ const renameOptionCallbackFactory = function (block: VariableBlock) { */ const deleteOptionCallbackFactory = function (block: VariableBlock) { return function () { - const variableField = block.getField('VAR') as FieldVariable; - const variable = variableField.getVariable(); + const variableField = block.getField('VAR') as FieldVariable + const variable = variableField.getVariable() if (variable) { - Variables.deleteVariable(variable.getWorkspace(), variable, block); + Variables.deleteVariable(variable.getWorkspace(), variable, block) } - }; -}; + } +} Extensions.registerMixin( 'contextMenu_variableDynamicSetterGetter', CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN, -); +) // Register provided blocks. -defineBlocks(blocks); +defineBlocks(blocks) From 25ba1b29d07d9d50e0da0dab6dfa3d5769275f78 Mon Sep 17 00:00:00 2001 From: heliacer Date: Wed, 17 Dec 2025 15:52:38 +0100 Subject: [PATCH 07/12] fix: prettier problems --- core/interfaces/i_json_block_definition.ts | 92 +++++++++++----------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/core/interfaces/i_json_block_definition.ts b/core/interfaces/i_json_block_definition.ts index 82385a94c8e..a9b2a71f9d2 100644 --- a/core/interfaces/i_json_block_definition.ts +++ b/core/interfaces/i_json_block_definition.ts @@ -4,32 +4,32 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { FieldCheckboxFromJsonConfig } from "../field_checkbox" -import { FieldDropdownFromJsonConfig } from "../field_dropdown" -import { FieldImageFromJsonConfig } from "../field_image" -import { FieldNumberFromJsonConfig } from "../field_number" -import { FieldTextInputFromJsonConfig } from "../field_textinput" -import { FieldVariableFromJsonConfig } from "../field_variable" +import {FieldCheckboxFromJsonConfig} from '../field_checkbox.js'; +import {FieldDropdownFromJsonConfig} from '../field_dropdown'; +import {FieldImageFromJsonConfig} from '../field_image'; +import {FieldNumberFromJsonConfig} from '../field_number'; +import {FieldTextInputFromJsonConfig} from '../field_textinput'; +import {FieldVariableFromJsonConfig} from '../field_variable'; export interface JsonBlockDefinition { - type?: string - style?: string - colour?: string | number - output?: string | string[] | null - previousStatement?: string | string[] | null - nextStatement?: string | string[] | null - outputShape?: number - inputsInline?: boolean - tooltip?: string - helpUrl?: string - extensions?: string[] - mutator?: string - enableContextMenu?: boolean - suppressPrefixSuffix?: boolean + type?: string; + style?: string; + colour?: string | number; + output?: string | string[] | null; + previousStatement?: string | string[] | null; + nextStatement?: string | string[] | null; + outputShape?: number; + inputsInline?: boolean; + tooltip?: string; + helpUrl?: string; + extensions?: string[]; + mutator?: string; + enableContextMenu?: boolean; + suppressPrefixSuffix?: boolean; - [key: `message${number}`]: string | undefined - [key: `args${number}`]: BlockArg[] | undefined - [key: `implicitAlign${number}`]: string | undefined + [key: `message${number}`]: string | undefined; + [key: `args${number}`]: BlockArg[] | undefined; + [key: `implicitAlign${number}`]: string | undefined; } /** Block Arg */ @@ -42,54 +42,54 @@ export type BlockArg = | FieldDropdownArg | FieldCheckboxArg | FieldImageArg - | FieldVariableArg + | FieldVariableArg; /** Input Args */ interface InputValueArg { - name: string - type: 'input_value' - check?: string | string[] - align?: FieldsAlign + name: string; + type: 'input_value'; + check?: string | string[]; + align?: FieldsAlign; } interface InputStatementArg { - name: string - type: 'input_statement' - check?: string | string[] + name: string; + type: 'input_statement'; + check?: string | string[]; } interface InputDummyArg { - name?: string - type: 'input_dummy' + name?: string; + type: 'input_dummy'; } /** Field Args */ interface FieldInputArg extends FieldTextInputFromJsonConfig { - name: string - type: 'field_input' + name: string; + type: 'field_input'; } interface FieldNumberArg extends FieldNumberFromJsonConfig { - name: string - type: 'field_number' + name: string; + type: 'field_number'; } interface FieldDropdownArg extends FieldDropdownFromJsonConfig { - name: string - type: 'field_dropdown' + name: string; + type: 'field_dropdown'; } interface FieldCheckboxArg extends FieldCheckboxFromJsonConfig { - name: string - type: 'field_checkbox' + name: string; + type: 'field_checkbox'; } interface FieldImageArg extends FieldImageFromJsonConfig { - name: string - type: 'field_image' + name: string; + type: 'field_image'; } interface FieldVariableArg extends FieldVariableFromJsonConfig { - name: string - type: 'field_variable' + name: string; + type: 'field_variable'; } -export type FieldsAlign = 'LEFT' | 'RIGHT' | 'CENTRE' +export type FieldsAlign = 'LEFT' | 'RIGHT' | 'CENTRE'; From f62c62ec13a51c774916cac036d11d8e01a8b166 Mon Sep 17 00:00:00 2001 From: heliacer Date: Wed, 17 Dec 2025 20:57:57 +0100 Subject: [PATCH 08/12] fix: proposed changes --- blocks/logic.ts | 300 ++++++++++----------- blocks/variables.ts | 85 +++--- blocks/variables_dynamic.ts | 94 +++---- core/block.ts | 19 +- core/common.ts | 2 +- core/interfaces/i_json_block_definition.ts | 41 ++- 6 files changed, 285 insertions(+), 256 deletions(-) diff --git a/blocks/logic.ts b/blocks/logic.ts index 89dbf631c43..d2a7405fffa 100644 --- a/blocks/logic.ts +++ b/blocks/logic.ts @@ -6,22 +6,22 @@ // Former goog.module ID: Blockly.libraryBlocks.logic -import type { Block } from '../core/block.js' -import type { BlockSvg } from '../core/block_svg.js' +import type {Block} from '../core/block.js'; +import type {BlockSvg} from '../core/block_svg.js'; import { createBlockDefinitionsFromJsonArray, defineBlocks, -} from '../core/common.js' -import type { Connection } from '../core/connection.js' -import * as Events from '../core/events/events.js' -import type { Abstract as AbstractEvent } from '../core/events/events_abstract.js' -import * as Extensions from '../core/extensions.js' -import '../core/field_dropdown.js' -import '../core/field_label.js' -import '../core/icons/mutator_icon.js' -import { Msg } from '../core/msg.js' -import * as xmlUtils from '../core/utils/xml.js' -import type { Workspace } from '../core/workspace.js' +} from '../core/common.js'; +import type {Connection} from '../core/connection.js'; +import * as Events from '../core/events/events.js'; +import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js'; +import * as Extensions from '../core/extensions.js'; +import '../core/field_dropdown.js'; +import '../core/field_label.js'; +import '../core/icons/mutator_icon.js'; +import {Msg} from '../core/msg.js'; +import * as xmlUtils from '../core/utils/xml.js'; +import type {Workspace} from '../core/workspace.js'; /** * A dictionary of the block definitions provided by this module. @@ -251,7 +251,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'style': 'logic_blocks', 'tooltip': '%{BKY_CONTROLS_IF_ELSE_TOOLTIP}', }, -]) +]); /** * Tooltip text, keyed by block OP value. Used by logic_compare and @@ -271,33 +271,33 @@ const TOOLTIPS_BY_OP = { // logic_operation 'AND': '%{BKY_LOGIC_OPERATION_TOOLTIP_AND}', 'OR': '%{BKY_LOGIC_OPERATION_TOOLTIP_OR}', -} +}; Extensions.register( 'logic_op_tooltip', Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP), -) +); /** Type of a block that has CONTROLS_IF_MUTATOR_MIXIN */ -type IfBlock = Block & IfMixin -interface IfMixin extends IfMixinType { } -type IfMixinType = typeof CONTROLS_IF_MUTATOR_MIXIN +type IfBlock = Block & IfMixin; +interface IfMixin extends IfMixinType {} +type IfMixinType = typeof CONTROLS_IF_MUTATOR_MIXIN; // Types for quarks defined in JSON. /** Type of a controls_if_if (if mutator container) block. */ -interface ContainerBlock extends Block { } +interface ContainerBlock extends Block {} /** Type of a controls_if_elseif or controls_if_else block. */ interface ClauseBlock extends Block { - valueConnection_?: Connection | null - statementConnection_?: Connection | null + valueConnection_?: Connection | null; + statementConnection_?: Connection | null; } /** Extra state for serialising controls_if blocks. */ type IfExtraState = { - elseIfCount?: number - hasElse?: boolean -} + elseIfCount?: number; + hasElse?: boolean; +}; /** * Mutator methods added to controls_if blocks. @@ -314,16 +314,16 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ mutationToDom: function (this: IfBlock): Element | null { if (!this.elseifCount_ && !this.elseCount_) { - return null + return null; } - const container = xmlUtils.createElement('mutation') + const container = xmlUtils.createElement('mutation'); if (this.elseifCount_) { - container.setAttribute('elseif', String(this.elseifCount_)) + container.setAttribute('elseif', String(this.elseifCount_)); } if (this.elseCount_) { - container.setAttribute('else', '1') + container.setAttribute('else', '1'); } - return container + return container; }, /** * Parse XML to restore the else-if and else inputs. @@ -332,9 +332,9 @@ const CONTROLS_IF_MUTATOR_MIXIN = { * @param xmlElement XML storage element. */ domToMutation: function (this: IfBlock, xmlElement: Element) { - this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif')!, 10) || 0 - this.elseCount_ = parseInt(xmlElement.getAttribute('else')!, 10) || 0 - this.rebuildShape_() + this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif')!, 10) || 0; + this.elseCount_ = parseInt(xmlElement.getAttribute('else')!, 10) || 0; + this.rebuildShape_(); }, /** * Returns the state of this block as a JSON serializable object. @@ -343,16 +343,16 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ saveExtraState: function (this: IfBlock): IfExtraState | null { if (!this.elseifCount_ && !this.elseCount_) { - return null + return null; } - const state = Object.create(null) + const state = Object.create(null); if (this.elseifCount_) { - state['elseIfCount'] = this.elseifCount_ + state['elseIfCount'] = this.elseifCount_; } if (this.elseCount_) { - state['hasElse'] = true + state['hasElse'] = true; } - return state + return state; }, /** * Applies the given state to this block. @@ -362,9 +362,9 @@ const CONTROLS_IF_MUTATOR_MIXIN = { * else state. */ loadExtraState: function (this: IfBlock, state: IfExtraState) { - this.elseifCount_ = state['elseIfCount'] || 0 - this.elseCount_ = state['hasElse'] ? 1 : 0 - this.updateShape_() + this.elseifCount_ = state['elseIfCount'] || 0; + this.elseCount_ = state['hasElse'] ? 1 : 0; + this.updateShape_(); }, /** * Populate the mutator's dialog with this block's components. @@ -374,20 +374,20 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ decompose: function (this: IfBlock, workspace: Workspace): ContainerBlock { const containerBlock = workspace.newBlock('controls_if_if'); - (containerBlock as BlockSvg).initSvg() - let connection = containerBlock.nextConnection! + (containerBlock as BlockSvg).initSvg(); + let connection = containerBlock.nextConnection!; for (let i = 1; i <= this.elseifCount_; i++) { const elseifBlock = workspace.newBlock('controls_if_elseif'); - (elseifBlock as BlockSvg).initSvg() - connection.connect(elseifBlock.previousConnection!) - connection = elseifBlock.nextConnection! + (elseifBlock as BlockSvg).initSvg(); + connection.connect(elseifBlock.previousConnection!); + connection = elseifBlock.nextConnection!; } if (this.elseCount_) { const elseBlock = workspace.newBlock('controls_if_else'); - (elseBlock as BlockSvg).initSvg() - connection.connect(elseBlock.previousConnection!) + (elseBlock as BlockSvg).initSvg(); + connection.connect(elseBlock.previousConnection!); } - return containerBlock + return containerBlock; }, /** * Reconfigure this block based on the mutator dialog's components. @@ -396,49 +396,49 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ compose: function (this: IfBlock, containerBlock: ContainerBlock) { let clauseBlock = - containerBlock.nextConnection!.targetBlock() as ClauseBlock | null + containerBlock.nextConnection!.targetBlock() as ClauseBlock | null; // Count number of inputs. - this.elseifCount_ = 0 - this.elseCount_ = 0 + this.elseifCount_ = 0; + this.elseCount_ = 0; // Connections arrays are passed to .reconnectChildBlocks_() which // takes 1-based arrays, so are initialised with a dummy value at // index 0 for convenience. - const valueConnections: Array = [null] - const statementConnections: Array = [null] - let elseStatementConnection: Connection | null = null + const valueConnections: Array = [null]; + const statementConnections: Array = [null]; + let elseStatementConnection: Connection | null = null; while (clauseBlock) { if (clauseBlock.isInsertionMarker()) { - clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null - continue + clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null; + continue; } switch (clauseBlock.type) { case 'controls_if_elseif': - this.elseifCount_++ + this.elseifCount_++; // TODO(#6920): null valid, undefined not. valueConnections.push( clauseBlock.valueConnection_ as Connection | null, - ) + ); statementConnections.push( clauseBlock.statementConnection_ as Connection | null, - ) - break + ); + break; case 'controls_if_else': - this.elseCount_++ + this.elseCount_++; elseStatementConnection = - clauseBlock.statementConnection_ as Connection | null - break + clauseBlock.statementConnection_ as Connection | null; + break; default: - throw TypeError('Unknown block type: ' + clauseBlock.type) + throw TypeError('Unknown block type: ' + clauseBlock.type); } - clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null + clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null; } - this.updateShape_() + this.updateShape_(); // Reconnect any child blocks. this.reconnectChildBlocks_( valueConnections, statementConnections, elseStatementConnection, - ) + ); }, /** * Store pointers to any connected child blocks. @@ -447,60 +447,60 @@ const CONTROLS_IF_MUTATOR_MIXIN = { */ saveConnections: function (this: IfBlock, containerBlock: ContainerBlock) { let clauseBlock = - containerBlock!.nextConnection!.targetBlock() as ClauseBlock | null - let i = 1 + containerBlock!.nextConnection!.targetBlock() as ClauseBlock | null; + let i = 1; while (clauseBlock) { if (clauseBlock.isInsertionMarker()) { - clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null - continue + clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null; + continue; } switch (clauseBlock.type) { case 'controls_if_elseif': { - const inputIf = this.getInput('IF' + i) - const inputDo = this.getInput('DO' + i) + const inputIf = this.getInput('IF' + i); + const inputDo = this.getInput('DO' + i); clauseBlock.valueConnection_ = - inputIf && inputIf.connection!.targetConnection + inputIf && inputIf.connection!.targetConnection; clauseBlock.statementConnection_ = - inputDo && inputDo.connection!.targetConnection - i++ - break + inputDo && inputDo.connection!.targetConnection; + i++; + break; } case 'controls_if_else': { - const inputDo = this.getInput('ELSE') + const inputDo = this.getInput('ELSE'); clauseBlock.statementConnection_ = - inputDo && inputDo.connection!.targetConnection - break + inputDo && inputDo.connection!.targetConnection; + break; } default: - throw TypeError('Unknown block type: ' + clauseBlock.type) + throw TypeError('Unknown block type: ' + clauseBlock.type); } - clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null + clauseBlock = clauseBlock.getNextBlock() as ClauseBlock | null; } }, /** * Reconstructs the block with all child blocks attached. */ rebuildShape_: function (this: IfBlock) { - const valueConnections: Array = [null] - const statementConnections: Array = [null] - let elseStatementConnection: Connection | null = null + const valueConnections: Array = [null]; + const statementConnections: Array = [null]; + let elseStatementConnection: Connection | null = null; if (this.getInput('ELSE')) { elseStatementConnection = - this.getInput('ELSE')!.connection!.targetConnection + this.getInput('ELSE')!.connection!.targetConnection; } for (let i = 1; this.getInput('IF' + i); i++) { - const inputIf = this.getInput('IF' + i) - const inputDo = this.getInput('DO' + i) - valueConnections.push(inputIf!.connection!.targetConnection) - statementConnections.push(inputDo!.connection!.targetConnection) + const inputIf = this.getInput('IF' + i); + const inputDo = this.getInput('DO' + i); + valueConnections.push(inputIf!.connection!.targetConnection); + statementConnections.push(inputDo!.connection!.targetConnection); } - this.updateShape_() + this.updateShape_(); this.reconnectChildBlocks_( valueConnections, statementConnections, elseStatementConnection, - ) + ); }, /** * Modify this block to have the correct number of inputs. @@ -510,25 +510,25 @@ const CONTROLS_IF_MUTATOR_MIXIN = { updateShape_: function (this: IfBlock) { // Delete everything. if (this.getInput('ELSE')) { - this.removeInput('ELSE') + this.removeInput('ELSE'); } for (let i = 1; this.getInput('IF' + i); i++) { - this.removeInput('IF' + i) - this.removeInput('DO' + i) + this.removeInput('IF' + i); + this.removeInput('DO' + i); } // Rebuild block. for (let i = 1; i <= this.elseifCount_; i++) { this.appendValueInput('IF' + i) .setCheck('Boolean') - .appendField(Msg['CONTROLS_IF_MSG_ELSEIF']) + .appendField(Msg['CONTROLS_IF_MSG_ELSEIF']); this.appendStatementInput('DO' + i).appendField( Msg['CONTROLS_IF_MSG_THEN'], - ) + ); } if (this.elseCount_) { this.appendStatementInput('ELSE').appendField( Msg['CONTROLS_IF_MSG_ELSE'], - ) + ); } }, /** @@ -547,19 +547,19 @@ const CONTROLS_IF_MUTATOR_MIXIN = { elseStatementConnection: Connection | null, ) { for (let i = 1; i <= this.elseifCount_; i++) { - valueConnections[i]?.reconnect(this, 'IF' + i) - statementConnections[i]?.reconnect(this, 'DO' + i) + valueConnections[i]?.reconnect(this, 'IF' + i); + statementConnections[i]?.reconnect(this, 'DO' + i); } - elseStatementConnection?.reconnect(this, 'ELSE') + elseStatementConnection?.reconnect(this, 'ELSE'); }, -} +}; Extensions.registerMutator( 'controls_if_mutator', CONTROLS_IF_MUTATOR_MIXIN, null as unknown as undefined, // TODO(#6920) ['controls_if_elseif', 'controls_if_else'], -) +); /** * "controls_if" extension function. Adds mutator, shape updating methods, @@ -569,27 +569,27 @@ const CONTROLS_IF_TOOLTIP_EXTENSION = function (this: IfBlock) { this.setTooltip( function (this: IfBlock) { if (!this.elseifCount_ && !this.elseCount_) { - return Msg['CONTROLS_IF_TOOLTIP_1'] + return Msg['CONTROLS_IF_TOOLTIP_1']; } else if (!this.elseifCount_ && this.elseCount_) { - return Msg['CONTROLS_IF_TOOLTIP_2'] + return Msg['CONTROLS_IF_TOOLTIP_2']; } else if (this.elseifCount_ && !this.elseCount_) { - return Msg['CONTROLS_IF_TOOLTIP_3'] + return Msg['CONTROLS_IF_TOOLTIP_3']; } else if (this.elseifCount_ && this.elseCount_) { - return Msg['CONTROLS_IF_TOOLTIP_4'] + return Msg['CONTROLS_IF_TOOLTIP_4']; } - return '' + return ''; }.bind(this), - ) -} + ); +}; -Extensions.register('controls_if_tooltip', CONTROLS_IF_TOOLTIP_EXTENSION) +Extensions.register('controls_if_tooltip', CONTROLS_IF_TOOLTIP_EXTENSION); /** Type of a block that has LOGIC_COMPARE_ONCHANGE_MIXIN */ -type CompareBlock = Block & CompareMixin +type CompareBlock = Block & CompareMixin; interface CompareMixin extends CompareMixinType { - prevBlocks_?: Array + prevBlocks_?: Array; } -type CompareMixinType = typeof LOGIC_COMPARE_ONCHANGE_MIXIN +type CompareMixinType = typeof LOGIC_COMPARE_ONCHANGE_MIXIN; /** * Adds dynamic type validation for the left and right sides of a @@ -604,11 +604,11 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = { */ onchange: function (this: CompareBlock, e: AbstractEvent) { if (!this.prevBlocks_) { - this.prevBlocks_ = [null, null] + this.prevBlocks_ = [null, null]; } - const blockA = this.getInputTargetBlock('A') - const blockB = this.getInputTargetBlock('B') + const blockA = this.getInputTargetBlock('A'); + const blockB = this.getInputTargetBlock('B'); // Disconnect blocks that existed prior to this change if they don't // match. if ( @@ -621,30 +621,30 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = { ) { // Mismatch between two inputs. Revert the block connections, // bumping away the newly connected block(s). - Events.setGroup(e.group) - const prevA = this.prevBlocks_[0] + Events.setGroup(e.group); + const prevA = this.prevBlocks_[0]; if (prevA !== blockA) { - blockA.unplug() + blockA.unplug(); if (prevA && !prevA.isDisposed() && !prevA.isShadow()) { // The shadow block is automatically replaced during unplug(). - this.getInput('A')!.connection!.connect(prevA.outputConnection!) + this.getInput('A')!.connection!.connect(prevA.outputConnection!); } } - const prevB = this.prevBlocks_[1] + const prevB = this.prevBlocks_[1]; if (prevB !== blockB) { - blockB.unplug() + blockB.unplug(); if (prevB && !prevB.isDisposed() && !prevB.isShadow()) { // The shadow block is automatically replaced during unplug(). - this.getInput('B')!.connection!.connect(prevB.outputConnection!) + this.getInput('B')!.connection!.connect(prevB.outputConnection!); } } - this.bumpNeighbours() - Events.setGroup(false) + this.bumpNeighbours(); + Events.setGroup(false); } - this.prevBlocks_[0] = this.getInputTargetBlock('A') - this.prevBlocks_[1] = this.getInputTargetBlock('B') + this.prevBlocks_[0] = this.getInputTargetBlock('A'); + this.prevBlocks_[1] = this.getInputTargetBlock('B'); }, -} +}; /** * "logic_compare" extension function. Adds type left and right side type @@ -652,15 +652,15 @@ const LOGIC_COMPARE_ONCHANGE_MIXIN = { */ const LOGIC_COMPARE_EXTENSION = function (this: CompareBlock) { // Add onchange handler to ensure types are compatible. - this.mixin(LOGIC_COMPARE_ONCHANGE_MIXIN) -} + this.mixin(LOGIC_COMPARE_ONCHANGE_MIXIN); +}; -Extensions.register('logic_compare', LOGIC_COMPARE_EXTENSION) +Extensions.register('logic_compare', LOGIC_COMPARE_EXTENSION); /** Type of a block that has LOGIC_TERNARY_ONCHANGE_MIXIN */ -type TernaryBlock = Block & TernaryMixin -interface TernaryMixin extends TernaryMixinType { } -type TernaryMixinType = typeof LOGIC_TERNARY_ONCHANGE_MIXIN +type TernaryBlock = Block & TernaryMixin; +interface TernaryMixin extends TernaryMixinType {} +type TernaryMixinType = typeof LOGIC_TERNARY_ONCHANGE_MIXIN; /** * Adds type coordination between inputs and output. @@ -673,14 +673,14 @@ const LOGIC_TERNARY_ONCHANGE_MIXIN = { * Prevent mismatched types. */ onchange: function (this: TernaryBlock, e: AbstractEvent) { - const blockA = this.getInputTargetBlock('THEN') - const blockB = this.getInputTargetBlock('ELSE') - const parentConnection = this.outputConnection!.targetConnection + const blockA = this.getInputTargetBlock('THEN'); + const blockB = this.getInputTargetBlock('ELSE'); + const parentConnection = this.outputConnection!.targetConnection; // Disconnect blocks that existed prior to this change if they don't // match. if ((blockA || blockB) && parentConnection) { for (let i = 0; i < 2; i++) { - const block = i === 1 ? blockA : blockB + const block = i === 1 ? blockA : blockB; if ( block && !block.workspace.connectionChecker.doTypeChecks( @@ -690,23 +690,23 @@ const LOGIC_TERNARY_ONCHANGE_MIXIN = { ) { // Ensure that any disconnections are grouped with the causing // event. - Events.setGroup(e.group) + Events.setGroup(e.group); if (parentConnection === this.prevParentConnection_) { - this.unplug() - parentConnection.getSourceBlock().bumpNeighbours() + this.unplug(); + parentConnection.getSourceBlock().bumpNeighbours(); } else { - block.unplug() - block.bumpNeighbours() + block.unplug(); + block.bumpNeighbours(); } - Events.setGroup(false) + Events.setGroup(false); } } } - this.prevParentConnection_ = parentConnection + this.prevParentConnection_ = parentConnection; }, -} +}; -Extensions.registerMixin('logic_ternary', LOGIC_TERNARY_ONCHANGE_MIXIN) +Extensions.registerMixin('logic_ternary', LOGIC_TERNARY_ONCHANGE_MIXIN); // Register provided blocks. -defineBlocks(blocks) +defineBlocks(blocks); diff --git a/blocks/variables.ts b/blocks/variables.ts index 725376165fa..4f1f640fa81 100644 --- a/blocks/variables.ts +++ b/blocks/variables.ts @@ -6,21 +6,21 @@ // Former goog.module ID: Blockly.libraryBlocks.variables -import type { Block } from '../core/block.js' +import type {Block} from '../core/block.js'; import { createBlockDefinitionsFromJsonArray, defineBlocks, -} from '../core/common.js' -import * as ContextMenu from '../core/contextmenu.js' +} from '../core/common.js'; +import * as ContextMenu from '../core/contextmenu.js'; import type { ContextMenuOption, LegacyContextMenuOption, -} from '../core/contextmenu_registry.js' -import * as Extensions from '../core/extensions.js' -import '../core/field_label.js' -import { FieldVariable } from '../core/field_variable.js' -import { Msg } from '../core/msg.js' -import * as Variables from '../core/variables.js' +} from '../core/contextmenu_registry.js'; +import * as Extensions from '../core/extensions.js'; +import '../core/field_label.js'; +import {FieldVariable} from '../core/field_variable.js'; +import {Msg} from '../core/msg.js'; +import * as Variables from '../core/variables.js'; /** * A dictionary of the block definitions provided by this module. @@ -60,19 +60,18 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ], 'previousStatement': null, 'nextStatement': null, - 'output': null, 'style': 'variable_blocks', 'tooltip': '%{BKY_VARIABLES_SET_TOOLTIP}', 'helpUrl': '%{BKY_VARIABLES_SET_HELPURL}', 'extensions': ['contextMenu_variableSetterGetter'], }, -]) +]); /** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */ -type VariableBlock = Block & VariableMixin -interface VariableMixin extends VariableMixinType { } +type VariableBlock = Block & VariableMixin; +interface VariableMixin extends VariableMixinType {} type VariableMixinType = - typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN + typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN; /** * Mixin to add context menu items to create getter/setter blocks for this @@ -90,28 +89,28 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { options: Array, ) { if (!this.isInFlyout) { - let oppositeType - let contextMenuMsg + let oppositeType; + let contextMenuMsg; // Getter blocks have the option to create a setter block, and vice versa. if (this.type === 'variables_get') { - oppositeType = 'variables_set' - contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET'] + oppositeType = 'variables_set'; + contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET']; } else { - oppositeType = 'variables_get' - contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET'] + oppositeType = 'variables_get'; + contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET']; } - const varField = this.getField('VAR')! + const varField = this.getField('VAR')!; const newVarBlockState = { type: oppositeType, - fields: { VAR: varField.saveState(true) }, - } + fields: {VAR: varField.saveState(true)}, + }; options.push({ enabled: this.workspace.remainingCapacity() > 0, text: contextMenuMsg.replace('%1', varField.getText()), callback: ContextMenu.callbackFactory(this, newVarBlockState), - }) + }); // Getter blocks have the option to rename or delete that variable. } else { if ( @@ -122,19 +121,19 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { text: Msg['RENAME_VARIABLE'], enabled: true, callback: renameOptionCallbackFactory(this), - } - const name = this.getField('VAR')!.getText() + }; + const name = this.getField('VAR')!.getText(); const deleteOption = { text: Msg['DELETE_VARIABLE'].replace('%1', name), enabled: true, callback: deleteOptionCallbackFactory(this), - } - options.unshift(renameOption) - options.unshift(deleteOption) + }; + options.unshift(renameOption); + options.unshift(deleteOption); } } }, -} +}; /** * Factory for callbacks for rename variable dropdown menu option @@ -147,12 +146,12 @@ const renameOptionCallbackFactory = function ( block: VariableBlock, ): () => void { return function () { - const workspace = block.workspace - const variableField = block.getField('VAR') as FieldVariable - const variable = variableField.getVariable()! - Variables.renameVariable(workspace, variable) - } -} + const workspace = block.workspace; + const variableField = block.getField('VAR') as FieldVariable; + const variable = variableField.getVariable()!; + Variables.renameVariable(workspace, variable); + }; +}; /** * Factory for callbacks for delete variable dropdown menu option @@ -165,18 +164,18 @@ const deleteOptionCallbackFactory = function ( block: VariableBlock, ): () => void { return function () { - const variableField = block.getField('VAR') as FieldVariable - const variable = variableField.getVariable() + const variableField = block.getField('VAR') as FieldVariable; + const variable = variableField.getVariable(); if (variable) { - Variables.deleteVariable(variable.getWorkspace(), variable, block) + Variables.deleteVariable(variable.getWorkspace(), variable, block); } - } -} + }; +}; Extensions.registerMixin( 'contextMenu_variableSetterGetter', CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN, -) +); // Register provided blocks. -defineBlocks(blocks) +defineBlocks(blocks); diff --git a/blocks/variables_dynamic.ts b/blocks/variables_dynamic.ts index b4b23039fe2..f50f029fcb6 100644 --- a/blocks/variables_dynamic.ts +++ b/blocks/variables_dynamic.ts @@ -6,22 +6,22 @@ // Former goog.module ID: Blockly.libraryBlocks.variablesDynamic -import type { Block } from '../core/block.js' +import type {Block} from '../core/block.js'; import { createBlockDefinitionsFromJsonArray, defineBlocks, -} from '../core/common.js' -import * as ContextMenu from '../core/contextmenu.js' +} from '../core/common.js'; +import * as ContextMenu from '../core/contextmenu.js'; import type { ContextMenuOption, LegacyContextMenuOption, -} from '../core/contextmenu_registry.js' -import { Abstract as AbstractEvent } from '../core/events/events_abstract.js' -import * as Extensions from '../core/extensions.js' -import '../core/field_label.js' -import { FieldVariable } from '../core/field_variable.js' -import { Msg } from '../core/msg.js' -import * as Variables from '../core/variables.js' +} from '../core/contextmenu_registry.js'; +import {Abstract as AbstractEvent} from '../core/events/events_abstract.js'; +import * as Extensions from '../core/extensions.js'; +import '../core/field_label.js'; +import {FieldVariable} from '../core/field_variable.js'; +import {Msg} from '../core/msg.js'; +import * as Variables from '../core/variables.js'; /** * A dictionary of the block definitions provided by this module. @@ -66,13 +66,13 @@ export const blocks = createBlockDefinitionsFromJsonArray([ 'helpUrl': '%{BKY_VARIABLES_SET_HELPURL}', 'extensions': ['contextMenu_variableDynamicSetterGetter'], }, -]) +]); /** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */ -type VariableBlock = Block & VariableMixin -interface VariableMixin extends VariableMixinType { } +type VariableBlock = Block & VariableMixin; +interface VariableMixin extends VariableMixinType {} type VariableMixinType = - typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN + typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN; /** * Mixin to add context menu items to create getter/setter blocks for this @@ -91,27 +91,27 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { ) { // Getter blocks have the option to create a setter block, and vice versa. if (!this.isInFlyout) { - let oppositeType - let contextMenuMsg + let oppositeType; + let contextMenuMsg; if (this.type === 'variables_get_dynamic') { - oppositeType = 'variables_set_dynamic' - contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET'] + oppositeType = 'variables_set_dynamic'; + contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET']; } else { - oppositeType = 'variables_get_dynamic' - contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET'] + oppositeType = 'variables_get_dynamic'; + contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET']; } - const varField = this.getField('VAR')! + const varField = this.getField('VAR')!; const newVarBlockState = { type: oppositeType, - fields: { VAR: varField.saveState(true) }, - } + fields: {VAR: varField.saveState(true)}, + }; options.push({ enabled: this.workspace.remainingCapacity() > 0, text: contextMenuMsg.replace('%1', varField.getText()), callback: ContextMenu.callbackFactory(this, newVarBlockState), - }) + }); } else { if ( this.type === 'variables_get_dynamic' || @@ -121,15 +121,15 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { text: Msg['RENAME_VARIABLE'], enabled: true, callback: renameOptionCallbackFactory(this), - } - const name = this.getField('VAR')!.getText() + }; + const name = this.getField('VAR')!.getText(); const deleteOption = { text: Msg['DELETE_VARIABLE'].replace('%1', name), enabled: true, callback: deleteOptionCallbackFactory(this), - } - options.unshift(renameOption) - options.unshift(deleteOption) + }; + options.unshift(renameOption); + options.unshift(deleteOption); } } }, @@ -140,15 +140,15 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { * @param _e Change event. */ onchange: function (this: VariableBlock, _e: AbstractEvent) { - const id = this.getFieldValue('VAR') - const variableModel = Variables.getVariable(this.workspace, id)! + const id = this.getFieldValue('VAR'); + const variableModel = Variables.getVariable(this.workspace, id)!; if (this.type === 'variables_get_dynamic') { - this.outputConnection!.setCheck(variableModel.getType()) + this.outputConnection!.setCheck(variableModel.getType()); } else { - this.getInput('VALUE')!.connection!.setCheck(variableModel.getType()) + this.getInput('VALUE')!.connection!.setCheck(variableModel.getType()); } }, -} +}; /** * Factory for callbacks for rename variable dropdown menu option @@ -159,12 +159,12 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { */ const renameOptionCallbackFactory = function (block: VariableBlock) { return function () { - const workspace = block.workspace - const variableField = block.getField('VAR') as FieldVariable - const variable = variableField.getVariable()! - Variables.renameVariable(workspace, variable) - } -} + const workspace = block.workspace; + const variableField = block.getField('VAR') as FieldVariable; + const variable = variableField.getVariable()!; + Variables.renameVariable(workspace, variable); + }; +}; /** * Factory for callbacks for delete variable dropdown menu option @@ -175,18 +175,18 @@ const renameOptionCallbackFactory = function (block: VariableBlock) { */ const deleteOptionCallbackFactory = function (block: VariableBlock) { return function () { - const variableField = block.getField('VAR') as FieldVariable - const variable = variableField.getVariable() + const variableField = block.getField('VAR') as FieldVariable; + const variable = variableField.getVariable(); if (variable) { - Variables.deleteVariable(variable.getWorkspace(), variable, block) + Variables.deleteVariable(variable.getWorkspace(), variable, block); } - } -} + }; +}; Extensions.registerMixin( 'contextMenu_variableDynamicSetterGetter', CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN, -) +); // Register provided blocks. -defineBlocks(blocks) +defineBlocks(blocks); diff --git a/core/block.ts b/core/block.ts index 7070129705a..a74ba9517ed 100644 --- a/core/block.ts +++ b/core/block.ts @@ -42,6 +42,10 @@ import {StatementInput} from './inputs/statement_input.js'; import {ValueInput} from './inputs/value_input.js'; import {isCommentIcon} from './interfaces/i_comment_icon.js'; import {type IIcon} from './interfaces/i_icon.js'; +import { + BlockArg, + JsonBlockDefinition, +} from './interfaces/i_json_block_definition.js'; import type { IVariableModel, IVariableState, @@ -54,7 +58,6 @@ import * as idGenerator from './utils/idgenerator.js'; import * as parsing from './utils/parsing.js'; import {Size} from './utils/size.js'; import type {Workspace} from './workspace.js'; -import { BlockArg, JsonBlockDefinition } from './interfaces/i_json_block_definition.js' /** * Class for one block. @@ -1733,6 +1736,13 @@ export class Block { } // Set basic properties of block. + // Handle legacy style object format for backwards compatibility + if (json.style && typeof json.style === 'object') { + this.hat = (json.style as {hat?: string}).hat; + // Must set to null so it doesn't error when checking for style and + // colour. + json.style = null; + } if (json.style && json.colour) { throw Error(warningPrefix + 'Must not have both a colour and a style.'); } else if (json.style) { @@ -1747,7 +1757,8 @@ export class Block { this.interpolate( json[`message${i}`]!, json[`args${i}`] || [], - json[`implicitAlign${i}`], + // Backwards compatibility: lastDummyAlign aliases implicitAlign. + json[`implicitAlign${i}`] || (json as any)[`lastDummyAlign${i}`], warningPrefix, ); i++; @@ -1819,7 +1830,7 @@ export class Block { * @param warningPrefix Warning prefix string identifying block. */ private jsonInitColour(json: JsonBlockDefinition, warningPrefix: string) { - if (json.colour) { + if ('colour' in json) { if (json.colour === undefined) { console.warn(warningPrefix + 'Undefined colour value.'); } else { @@ -1840,7 +1851,7 @@ export class Block { * @param warningPrefix Warning prefix string identifying block. */ private jsonInitStyle(json: JsonBlockDefinition, warningPrefix: string) { - const blockStyleName = json.style! + const blockStyleName = json.style!; try { this.setStyle(blockStyleName); } catch { diff --git a/core/common.ts b/core/common.ts index 08882e9bad2..d15b1e3b8cc 100644 --- a/core/common.ts +++ b/core/common.ts @@ -13,7 +13,7 @@ import type {Connection} from './connection.js'; import {EventType} from './events/type.js'; import * as eventUtils from './events/utils.js'; import {getFocusManager} from './focus_manager.js'; -import {JsonBlockDefinition} from './interfaces/i_json_block_definition.js'; +import type {JsonBlockDefinition} from './interfaces/i_json_block_definition.js'; import {ISelectable, isSelectable} from './interfaces/i_selectable.js'; import {ShortcutRegistry} from './shortcut_registry.js'; import type {Workspace} from './workspace.js'; diff --git a/core/interfaces/i_json_block_definition.ts b/core/interfaces/i_json_block_definition.ts index a9b2a71f9d2..a9dcec11a18 100644 --- a/core/interfaces/i_json_block_definition.ts +++ b/core/interfaces/i_json_block_definition.ts @@ -11,9 +11,28 @@ import {FieldNumberFromJsonConfig} from '../field_number'; import {FieldTextInputFromJsonConfig} from '../field_textinput'; import {FieldVariableFromJsonConfig} from '../field_variable'; +/** + * Defines the JSON structure for a block definition in Blockly. + * + * @example + * ```typescript + * const blockDef: JsonBlockDefinition = { + * type: 'custom_block', + * message0: 'move %1 steps', + * args0: [ + * { + * 'type': 'field_number', + * 'name': 'INPUT', + * }, + * ], + * previousStatement: null, + * nextStatement: null, + * }; + * ``` + */ export interface JsonBlockDefinition { - type?: string; - style?: string; + type: string; + style?: string | null; colour?: string | number; output?: string | string[] | null; previousStatement?: string | string[] | null; @@ -46,50 +65,50 @@ export type BlockArg = /** Input Args */ interface InputValueArg { - name: string; type: 'input_value'; + name?: string; check?: string | string[]; align?: FieldsAlign; } interface InputStatementArg { - name: string; type: 'input_statement'; + name?: string; check?: string | string[]; } interface InputDummyArg { - name?: string; type: 'input_dummy'; + name?: string; } /** Field Args */ interface FieldInputArg extends FieldTextInputFromJsonConfig { - name: string; type: 'field_input'; + name?: string; } interface FieldNumberArg extends FieldNumberFromJsonConfig { - name: string; type: 'field_number'; + name?: string; } interface FieldDropdownArg extends FieldDropdownFromJsonConfig { - name: string; type: 'field_dropdown'; + name?: string; } interface FieldCheckboxArg extends FieldCheckboxFromJsonConfig { - name: string; type: 'field_checkbox'; + name?: string; } interface FieldImageArg extends FieldImageFromJsonConfig { - name: string; type: 'field_image'; + name?: string; } interface FieldVariableArg extends FieldVariableFromJsonConfig { - name: string; type: 'field_variable'; + name?: string; } export type FieldsAlign = 'LEFT' | 'RIGHT' | 'CENTRE'; From 93e0b4c0ede3840baf7fbd9813c945792d452038 Mon Sep 17 00:00:00 2001 From: heliacer Date: Thu, 18 Dec 2025 12:51:15 +0100 Subject: [PATCH 09/12] fix: revert to bracket access --- core/block.ts | 68 +++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/core/block.ts b/core/block.ts index a74ba9517ed..bc0dad51f65 100644 --- a/core/block.ts +++ b/core/block.ts @@ -1714,10 +1714,10 @@ export class Block { * @param json Structured data describing the block. */ jsonInit(json: JsonBlockDefinition) { - const warningPrefix = json.type ? 'Block "' + json.type + '": ' : ''; + const warningPrefix = json['type'] ? 'Block "' + json['type'] + '": ' : ''; // Validate inputs. - if (json.output && json.previousStatement) { + if (json['output'] && json['previousStatement']) { throw Error( warningPrefix + 'Must not have both an output and a previousStatement.', ); @@ -1737,15 +1737,15 @@ export class Block { // Set basic properties of block. // Handle legacy style object format for backwards compatibility - if (json.style && typeof json.style === 'object') { - this.hat = (json.style as {hat?: string}).hat; + if (json['style'] && typeof json['style'] === 'object') { + this.hat = (json['style'] as {hat?: string}).hat; // Must set to null so it doesn't error when checking for style and // colour. - json.style = null; + json['style'] = null; } - if (json.style && json.colour) { + if (json['style'] && json['colour']) { throw Error(warningPrefix + 'Must not have both a colour and a style.'); - } else if (json.style) { + } else if (json['style']) { this.jsonInitStyle(json, warningPrefix); } else { this.jsonInitColour(json, warningPrefix); @@ -1764,58 +1764,58 @@ export class Block { i++; } - if (json.inputsInline !== undefined) { + if (json['inputsInline'] !== undefined) { eventUtils.disable(); - this.setInputsInline(json.inputsInline); + this.setInputsInline(json['inputsInline']); eventUtils.enable(); } // Set output and previous/next connections. - if (json.output !== undefined) { - this.setOutput(true, json.output); + if (json['output'] !== undefined) { + this.setOutput(true, json['output']); } - if (json.outputShape !== undefined) { - this.setOutputShape(json.outputShape); + if (json['outputShape'] !== undefined) { + this.setOutputShape(json['outputShape']); } - if (json.previousStatement !== undefined) { - this.setPreviousStatement(true, json.previousStatement); + if (json['previousStatement'] !== undefined) { + this.setPreviousStatement(true, json['previousStatement']); } - if (json.nextStatement !== undefined) { - this.setNextStatement(true, json.nextStatement); + if (json['nextStatement'] !== undefined) { + this.setNextStatement(true, json['nextStatement']); } - if (json.tooltip !== undefined) { - const rawValue = json.tooltip; + if (json['tooltip'] !== undefined) { + const rawValue = json['tooltip']; const localizedText = parsing.replaceMessageReferences(rawValue); this.setTooltip(localizedText); } - if (json.enableContextMenu !== undefined) { - this.contextMenu = !!json.enableContextMenu; + if (json['enableContextMenu'] !== undefined) { + this.contextMenu = !!json['enableContextMenu']; } - if (json.suppressPrefixSuffix !== undefined) { - this.suppressPrefixSuffix = !!json.suppressPrefixSuffix; + if (json['suppressPrefixSuffix'] !== undefined) { + this.suppressPrefixSuffix = !!json['suppressPrefixSuffix']; } - if (json.helpUrl !== undefined) { - const rawValue = json.helpUrl; + if (json['helpUrl'] !== undefined) { + const rawValue = json['helpUrl']; const localizedValue = parsing.replaceMessageReferences(rawValue); this.setHelpUrl(localizedValue); } - if (typeof json.extensions === 'string') { + if (typeof json['extensions'] === 'string') { console.warn( warningPrefix + "JSON attribute 'extensions' should be an array of" + " strings. Found raw string in JSON for '" + - json.type + + json['type'] + "' block.", ); - json.extensions = [json.extensions]; // Correct and continue. + json['extensions'] = [json['extensions']]; // Correct and continue. } // Add the mutator to the block. - if (json.mutator !== undefined) { - Extensions.apply(json.mutator, this, true); + if (json['mutator'] !== undefined) { + Extensions.apply(json['mutator'], this, true); } - const extensionNames = json.extensions; + const extensionNames = json['extensions']; if (Array.isArray(extensionNames)) { for (let j = 0; j < extensionNames.length; j++) { Extensions.apply(extensionNames[j], this, false); @@ -1831,10 +1831,10 @@ export class Block { */ private jsonInitColour(json: JsonBlockDefinition, warningPrefix: string) { if ('colour' in json) { - if (json.colour === undefined) { + if (json['colour'] === undefined) { console.warn(warningPrefix + 'Undefined colour value.'); } else { - const rawValue = json.colour; + const rawValue = json['colour']; try { this.setColour(rawValue); } catch { @@ -1851,7 +1851,7 @@ export class Block { * @param warningPrefix Warning prefix string identifying block. */ private jsonInitStyle(json: JsonBlockDefinition, warningPrefix: string) { - const blockStyleName = json.style!; + const blockStyleName = json['style']!; try { this.setStyle(blockStyleName); } catch { From 92de714c9a93e03eaae697a59572ba45150b1b9b Mon Sep 17 00:00:00 2001 From: heliacer Date: Thu, 18 Dec 2025 13:00:19 +0100 Subject: [PATCH 10/12] feat: add input_end_row field to JsonBlockDefinition interface --- core/interfaces/i_json_block_definition.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/interfaces/i_json_block_definition.ts b/core/interfaces/i_json_block_definition.ts index a9dcec11a18..90abedfe802 100644 --- a/core/interfaces/i_json_block_definition.ts +++ b/core/interfaces/i_json_block_definition.ts @@ -56,6 +56,7 @@ export type BlockArg = | InputValueArg | InputStatementArg | InputDummyArg + | InputEndRowArg | FieldInputArg | FieldNumberArg | FieldDropdownArg @@ -79,6 +80,10 @@ interface InputDummyArg { type: 'input_dummy'; name?: string; } +interface InputEndRowArg { + type: 'input_end_row'; + name?: string; +} /** Field Args */ interface FieldInputArg extends FieldTextInputFromJsonConfig { From 750c858a596df1bbcf197d6a4e81b7e19d5cfb63 Mon Sep 17 00:00:00 2001 From: heliacer Date: Thu, 18 Dec 2025 13:10:33 +0100 Subject: [PATCH 11/12] revert: make JsonBlockDefinition optional for now to avoid breaking change --- core/block.ts | 12 ++++-------- core/common.ts | 9 ++++----- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/core/block.ts b/core/block.ts index bc0dad51f65..0469ffa1c37 100644 --- a/core/block.ts +++ b/core/block.ts @@ -42,10 +42,6 @@ import {StatementInput} from './inputs/statement_input.js'; import {ValueInput} from './inputs/value_input.js'; import {isCommentIcon} from './interfaces/i_comment_icon.js'; import {type IIcon} from './interfaces/i_icon.js'; -import { - BlockArg, - JsonBlockDefinition, -} from './interfaces/i_json_block_definition.js'; import type { IVariableModel, IVariableState, @@ -1713,7 +1709,7 @@ export class Block { * * @param json Structured data describing the block. */ - jsonInit(json: JsonBlockDefinition) { + jsonInit(json: AnyDuringMigration) { const warningPrefix = json['type'] ? 'Block "' + json['type'] + '": ' : ''; // Validate inputs. @@ -1829,7 +1825,7 @@ export class Block { * @param json Structured data describing the block. * @param warningPrefix Warning prefix string identifying block. */ - private jsonInitColour(json: JsonBlockDefinition, warningPrefix: string) { + private jsonInitColour(json: AnyDuringMigration, warningPrefix: string) { if ('colour' in json) { if (json['colour'] === undefined) { console.warn(warningPrefix + 'Undefined colour value.'); @@ -1850,7 +1846,7 @@ export class Block { * @param json Structured data describing the block. * @param warningPrefix Warning prefix string identifying block. */ - private jsonInitStyle(json: JsonBlockDefinition, warningPrefix: string) { + private jsonInitStyle(json: AnyDuringMigration, warningPrefix: string) { const blockStyleName = json['style']!; try { this.setStyle(blockStyleName); @@ -1904,7 +1900,7 @@ export class Block { */ private interpolate( message: string, - args: BlockArg[], + args: AnyDuringMigration[], implicitAlign: string | undefined, warningPrefix: string, ) { diff --git a/core/common.ts b/core/common.ts index d15b1e3b8cc..734747b8823 100644 --- a/core/common.ts +++ b/core/common.ts @@ -13,7 +13,6 @@ import type {Connection} from './connection.js'; import {EventType} from './events/type.js'; import * as eventUtils from './events/utils.js'; import {getFocusManager} from './focus_manager.js'; -import type {JsonBlockDefinition} from './interfaces/i_json_block_definition.js'; import {ISelectable, isSelectable} from './interfaces/i_selectable.js'; import {ShortcutRegistry} from './shortcut_registry.js'; import type {Workspace} from './workspace.js'; @@ -239,7 +238,7 @@ export function getBlockTypeCounts( * @returns A function that calls jsonInit with the correct value * of jsonDef. */ -function jsonInitFactory(jsonDef: JsonBlockDefinition): () => void { +function jsonInitFactory(jsonDef: AnyDuringMigration): () => void { return function (this: Block) { this.jsonInit(jsonDef); }; @@ -251,14 +250,14 @@ function jsonInitFactory(jsonDef: JsonBlockDefinition): () => void { * * @param jsonArray An array of JSON block definitions. */ -export function defineBlocksWithJsonArray(jsonArray: JsonBlockDefinition[]) { +export function defineBlocksWithJsonArray(jsonArray: AnyDuringMigration[]) { TEST_ONLY.defineBlocksWithJsonArrayInternal(jsonArray); } /** * Private version of defineBlocksWithJsonArray for stubbing in tests. */ -function defineBlocksWithJsonArrayInternal(jsonArray: JsonBlockDefinition[]) { +function defineBlocksWithJsonArrayInternal(jsonArray: AnyDuringMigration[]) { defineBlocks(createBlockDefinitionsFromJsonArray(jsonArray)); } @@ -271,7 +270,7 @@ function defineBlocksWithJsonArrayInternal(jsonArray: JsonBlockDefinition[]) { * definitions created. */ export function createBlockDefinitionsFromJsonArray( - jsonArray: JsonBlockDefinition[], + jsonArray: AnyDuringMigration[], ): {[key: string]: BlockDefinition} { const blocks: {[key: string]: BlockDefinition} = {}; for (let i = 0; i < jsonArray.length; i++) { From 4b382370379803ab286b46133f302546bef28c0e Mon Sep 17 00:00:00 2001 From: heliacer Date: Thu, 18 Dec 2025 13:17:03 +0100 Subject: [PATCH 12/12] fix: final touches (I hope) --- core/block.ts | 4 ++-- core/common.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/block.ts b/core/block.ts index 0469ffa1c37..18e574ae13e 100644 --- a/core/block.ts +++ b/core/block.ts @@ -1751,7 +1751,7 @@ export class Block { let i = 0; while (json[`message${i}`] !== undefined) { this.interpolate( - json[`message${i}`]!, + json[`message${i}`] || '', json[`args${i}`] || [], // Backwards compatibility: lastDummyAlign aliases implicitAlign. json[`implicitAlign${i}`] || (json as any)[`lastDummyAlign${i}`], @@ -1847,7 +1847,7 @@ export class Block { * @param warningPrefix Warning prefix string identifying block. */ private jsonInitStyle(json: AnyDuringMigration, warningPrefix: string) { - const blockStyleName = json['style']!; + const blockStyleName = json['style']; try { this.setStyle(blockStyleName); } catch { diff --git a/core/common.ts b/core/common.ts index 734747b8823..7f23779ec93 100644 --- a/core/common.ts +++ b/core/common.ts @@ -279,7 +279,7 @@ export function createBlockDefinitionsFromJsonArray( console.warn(`Block definition #${i} in JSON array is ${elem}. Skipping`); continue; } - const type = elem.type; + const type = elem['type']; if (!type) { console.warn( `Block definition #${i} in JSON array is missing a type attribute. ` +