Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions blocks/logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'logic_null',
'message0': '%{BKY_LOGIC_NULL}',
Comment thread
heliacer marked this conversation as resolved.
'output': null,
'style': 'logic_blocks',
'tooltip': '%{BKY_LOGIC_NULL_TOOLTIP}',
'helpUrl': '%{BKY_LOGIC_NULL_HELPURL}',
Expand Down Expand Up @@ -217,7 +216,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([
'name': 'ELSE',
},
],
Comment thread
heliacer marked this conversation as resolved.
'output': null,
'style': 'logic_blocks',
'tooltip': '%{BKY_LOGIC_TERNARY_TOOLTIP}',
'helpUrl': '%{BKY_LOGIC_TERNARY_HELPURL}',
Expand Down
2 changes: 0 additions & 2 deletions blocks/loops.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'field_variable',
'name': 'VAR',
'variable': null,
},
{
'type': 'input_value',
Expand Down Expand Up @@ -167,7 +166,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'field_variable',
'name': 'VAR',
'variable': null,
},
{
'type': 'input_value',
Expand Down
1 change: 0 additions & 1 deletion blocks/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([
'variable': '%{BKY_VARIABLES_DEFAULT_NAME}',
},
],
'output': null,
'style': 'variable_blocks',
Comment thread
heliacer marked this conversation as resolved.
'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}',
'tooltip': '%{BKY_VARIABLES_GET_TOOLTIP}',
Expand Down
1 change: 0 additions & 1 deletion blocks/variables_dynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([
'variable': '%{BKY_VARIABLES_DEFAULT_NAME}',
},
],
Comment thread
heliacer marked this conversation as resolved.
'output': null,
'style': 'variable_dynamic_blocks',
'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}',
'tooltip': '%{BKY_VARIABLES_GET_TOOLTIP}',
Expand Down
94 changes: 43 additions & 51 deletions core/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Comment thread
heliacer marked this conversation as resolved.
Outdated

/**
* Class for one block.
Expand Down Expand Up @@ -1709,20 +1710,20 @@ 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.',
);
}

// 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}).`,
Expand All @@ -1732,87 +1733,78 @@ 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;
}
Comment thread
heliacer marked this conversation as resolved.

if (json['style'] && json['colour']) {
if (json.style && json.colour) {
throw Error(warningPrefix + 'Must not have both a colour and a style.');
Comment thread
heliacer marked this conversation as resolved.
Outdated
} else if (json['style']) {
} else if (json.style) {
this.jsonInitStyle(json, warningPrefix);
} else {
this.jsonInitColour(json, warningPrefix);
}

// 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],
Comment thread
heliacer marked this conversation as resolved.
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);
Expand All @@ -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) {
Comment thread
heliacer marked this conversation as resolved.
Outdated
if (json.colour === undefined) {
console.warn(warningPrefix + 'Undefined colour value.');
} else {
const rawValue = json['colour'];
const rawValue = json.colour;
try {
this.setColour(rawValue);
} catch {
Expand All @@ -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!
Comment thread
heliacer marked this conversation as resolved.
Outdated
try {
this.setStyle(blockStyleName);
} catch {
Expand Down Expand Up @@ -1901,7 +1893,7 @@ export class Block {
*/
private interpolate(
message: string,
args: AnyDuringMigration[],
args: BlockArg[],
implicitAlign: string | undefined,
warningPrefix: string,
) {
Expand Down
11 changes: 6 additions & 5 deletions core/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Comment thread
heliacer marked this conversation as resolved.
Outdated
import {ISelectable, isSelectable} from './interfaces/i_selectable.js';
import {ShortcutRegistry} from './shortcut_registry.js';
import type {Workspace} from './workspace.js';
Expand Down Expand Up @@ -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);
};
Expand All @@ -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));
}

Expand All @@ -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++) {
Expand All @@ -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. ` +
Expand Down
95 changes: 95 additions & 0 deletions core/interfaces/i_json_block_definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* @license
* Copyright 2025 Google LLC
* 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"
Comment thread
heliacer marked this conversation as resolved.
Outdated

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
}
Comment thread
heliacer marked this conversation as resolved.

/** Block Arg */
export type BlockArg =
| InputValueArg
| InputStatementArg
| InputDummyArg
| FieldInputArg
| FieldNumberArg
| FieldDropdownArg
| FieldCheckboxArg
| FieldImageArg
| FieldVariableArg
Comment thread
heliacer marked this conversation as resolved.
Outdated

/** Input Args */
interface InputValueArg {
name: string
Comment thread
heliacer marked this conversation as resolved.
Outdated
type: 'input_value'
check?: string | string[]
align?: FieldsAlign
}
interface InputStatementArg {
name: string
type: 'input_statement'
check?: string | string[]
}
interface InputDummyArg {
name?: string
type: 'input_dummy'
}
Comment thread
heliacer marked this conversation as resolved.

/** Field Args */
interface FieldInputArg extends FieldTextInputFromJsonConfig {
name: string
type: 'field_input'
}

interface FieldNumberArg extends FieldNumberFromJsonConfig {
name: string
type: 'field_number'
}

interface FieldDropdownArg extends FieldDropdownFromJsonConfig {
name: string
type: 'field_dropdown'
}

interface FieldCheckboxArg extends FieldCheckboxFromJsonConfig {
name: string
type: 'field_checkbox'
}

interface FieldImageArg extends FieldImageFromJsonConfig {
name: string
type: 'field_image'
}

interface FieldVariableArg extends FieldVariableFromJsonConfig {
name: string
type: 'field_variable'
}
Comment thread
heliacer marked this conversation as resolved.

export type FieldsAlign = 'LEFT' | 'RIGHT' | 'CENTRE'