From 568e6ffcdc41bbdc3bc0721350780eabd297b523 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Wed, 1 Oct 2025 10:35:12 -0700 Subject: [PATCH 1/7] fix: Fix bug that caused blocks to be inserted into an immovable stack. (#750) * fix: Fix bug that caused blocks to be inserted into an immovable stack. * fix: Fix bug that caused input blocks to bump immovable blocks on insert. --- src/navigation.ts | 59 ++++++++++++++++----- test/loadTestBlocks.js | 4 +- test/webdriverio/test/insert_test.ts | 79 ++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 14 deletions(-) diff --git a/src/navigation.ts b/src/navigation.ts index ed81790c..b4fa4d39 100644 --- a/src/navigation.ts +++ b/src/navigation.ts @@ -414,24 +414,59 @@ export class Navigation { const inputType = movingHasOutput ? Blockly.inputs.inputTypes.VALUE : Blockly.inputs.inputTypes.STATEMENT; - const compatibleInputs = stationaryNode.inputList.filter( - (input) => input.type === inputType, - ); - const input = compatibleInputs.length > 0 ? compatibleInputs[0] : null; - let connection = input?.connection; - if (connection) { + const compatibleConnections = stationaryNode.inputList + .filter((input) => input.type === inputType) + .map((input) => input.connection); + for (const connection of compatibleConnections) { + let targetConnection: Blockly.Connection | null | undefined = + connection; if (inputType === Blockly.inputs.inputTypes.STATEMENT) { - while (connection.targetBlock()?.nextConnection) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - connection = connection.targetBlock()!.nextConnection!; + while (targetConnection?.targetBlock()?.nextConnection) { + targetConnection = targetConnection?.targetBlock()?.nextConnection; } } - return connection as Blockly.RenderedConnection; + + if ( + targetConnection && + movingBlock.workspace.connectionChecker.canConnect( + movingHasOutput + ? movingBlock.outputConnection + : movingBlock.previousConnection, + targetConnection, + true, + // Since we're connecting programmatically, we don't care how + // close the blocks are when determining if they can be connected. + Infinity, + ) + ) { + return targetConnection as Blockly.RenderedConnection; + } } - // 2. Connect statement blocks to next connection. + // 2. Connect statement blocks to next connection. Only return a next + // connection to which the statement block can actually connect; some + // may be ineligible because they are e.g. in the middle of an immovable + // stack. if (stationaryNode.nextConnection && !movingHasOutput) { - return stationaryNode.nextConnection; + let nextConnection: Blockly.RenderedConnection | null = + stationaryNode.nextConnection; + while (nextConnection) { + if ( + movingBlock.workspace.connectionChecker.canConnect( + movingBlock.previousConnection, + nextConnection, + true, + // Since we're connecting programmatically, we don't care how + // close the blocks are when determining if they can be connected. + Infinity, + ) + ) { + return nextConnection; + } + nextConnection = + nextConnection.getSourceBlock().getNextBlock()?.nextConnection ?? + null; + } } // 3. Output connection. This will wrap around or displace. diff --git a/test/loadTestBlocks.js b/test/loadTestBlocks.js index 16f0ab81..c565e7fc 100644 --- a/test/loadTestBlocks.js +++ b/test/loadTestBlocks.js @@ -343,7 +343,7 @@ const moreBlocks = { 'DO0': { 'block': { 'type': 'text_print', - 'id': 'uSxT~QT8p%D2o)b~)Dki', + 'id': 'text_print_2', 'inputs': { 'TEXT': { 'shadow': { @@ -388,7 +388,7 @@ const moreBlocks = { 'next': { 'block': { 'type': 'text_print', - 'id': '-bTQ2YVSuBS/SYn[C^LX', + 'id': 'text_print_3', 'inputs': { 'TEXT': { 'shadow': { diff --git a/test/webdriverio/test/insert_test.ts b/test/webdriverio/test/insert_test.ts index 54d2eff2..6517cb46 100644 --- a/test/webdriverio/test/insert_test.ts +++ b/test/webdriverio/test/insert_test.ts @@ -5,6 +5,7 @@ */ import * as chai from 'chai'; +import * as Blockly from 'blockly'; import {Key} from 'webdriverio'; import { getFocusedBlockType, @@ -16,6 +17,7 @@ import { testSetup, sendKeyAndWait, keyRight, + keyDown, getCurrentFocusedBlockId, blockIsPresent, keyUp, @@ -103,4 +105,81 @@ suite('Insert test', function () { await getFocusedBlockType(this.browser), ); }); + + test('Does not insert between immovable blocks', async function () { + // Focus the create canvas block; we want to ensure that the newly + // inserted block is not attached to its next connection, because doing + // so would splice it into an immovable stack. + await focusOnBlock(this.browser, 'create_canvas_1'); + await this.browser.execute(() => { + Blockly.getMainWorkspace() + .getAllBlocks() + .forEach((b) => b.setMovable(false)); + }); + await tabNavigateToToolbox(this.browser); + + // Insert 'if' block + await keyRight(this.browser); + // Choose. + await sendKeyAndWait(this.browser, Key.Enter); + // Confirm position. + await sendKeyAndWait(this.browser, Key.Enter); + + // Assert inserted inside first block p5_setup not at top-level. + chai.assert.equal('controls_if', await getFocusedBlockType(this.browser)); + await keyUp(this.browser); + chai.assert.equal( + 'p5_background_color', + await getFocusedBlockType(this.browser), + ); + }); +}); + +suite('Insert test with more blocks', function () { + // Disable timeouts when non-zero PAUSE_TIME is used to watch tests run. + if (PAUSE_TIME) this.timeout(0); + + // Clear the workspace and load start blocks. + setup(async function () { + this.browser = await testSetup( + testFileLocations.MORE_BLOCKS, + this.timeout(), + ); + await this.browser.pause(PAUSE_TIME); + }); + + test('Does not bump immovable input blocks on insert', async function () { + // Focus the print block with a connected input block. Ordinarily, inserting + // an input block would connect it to this block and bump its child, but + // if all blocks are immovable the connected input block should not move + // and the newly inserted block should be added as a top-level block on the + // workspace. + await focusOnBlock(this.browser, 'text_print_2'); + await this.browser.execute(() => { + Blockly.getMainWorkspace() + .getAllBlocks() + .forEach((b) => b.setMovable(false)); + }); + await tabNavigateToToolbox(this.browser); + + // Insert number block + await keyDown(this.browser, 2); + await keyRight(this.browser); + // Choose. + await sendKeyAndWait(this.browser, Key.Enter); + // Confirm position. + await sendKeyAndWait(this.browser, Key.Enter); + + // Assert inserted at the top-level due to immovable block occupying the + // selected block's input. + chai.assert.equal('math_number', await getFocusedBlockType(this.browser)); + const focusedBlockIsParentless = await this.browser.execute(() => { + const focusedNode = Blockly.getFocusManager().getFocusedNode(); + return ( + focusedNode instanceof Blockly.BlockSvg && + focusedNode.getParent() === null + ); + }); + chai.assert.isTrue(focusedBlockIsParentless); + }); }); From b12710e7869a506417c3a8159a238b67cf7fa311 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Thu, 2 Oct 2025 14:45:59 -0700 Subject: [PATCH 2/7] fix: Fix the scenario picker with the advanced playground. (#747) * fix: Fix the scenario picker with the advanced playground. * Revert "fix(tests): Fix playground scenario selector by reverting PR #718 (#735)" This reverts commit f4560fdd0dc8d51b3f8a64562a18e9b50c2b0669. * chore: Update dev-tools. * chore: Remove unused import. --- package-lock.json | 333 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + test/index.html | 22 +-- test/index.ts | 88 ++++++------ 4 files changed, 373 insertions(+), 71 deletions(-) diff --git a/package-lock.json b/package-lock.json index b14dc185..4c066277 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "Apache-2.0", "devDependencies": { "@blockly/dev-scripts": "^4.0.8", + "@blockly/dev-tools": "^9.0.3", "@blockly/field-colour": "^6.0.2", "@eslint/eslintrc": "^2.1.2", "@eslint/js": "^8.49.0", @@ -323,6 +324,19 @@ "node": ">=6.9.0" } }, + "node_modules/@blockly/block-test": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@blockly/block-test/-/block-test-7.0.2.tgz", + "integrity": "sha512-fwbJnMiH4EoX/CR0ZTGzSKaGfpRBn4nudquoWfvG4ekkhTjaNTldDdHvUSeyexzvwZZcT6M4I1Jtq3IoomTKEg==", + "dev": true, + "license": "Apache 2.0", + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, "node_modules/@blockly/dev-scripts": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/@blockly/dev-scripts/-/dev-scripts-4.0.8.tgz", @@ -761,6 +775,117 @@ "node": ">=10" } }, + "node_modules/@blockly/dev-tools": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@blockly/dev-tools/-/dev-tools-9.0.3.tgz", + "integrity": "sha512-iJ0QZtupYqQ1VTqUDvy8yB4vZJto6cNaV73K7ExnkFm2WYA5NEmq+1Stbvts8j66EX1Qj4EyTucf4tPIqBHXYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@blockly/block-test": "^7.0.2", + "@blockly/theme-dark": "^8.0.1", + "@blockly/theme-deuteranopia": "^7.0.1", + "@blockly/theme-highcontrast": "^7.0.1", + "@blockly/theme-tritanopia": "^7.0.1", + "chai": "^4.2.0", + "dat.gui": "^0.7.7", + "lodash.assign": "^4.2.0", + "lodash.merge": "^4.6.2", + "monaco-editor": "^0.20.0", + "sinon": "^9.0.2" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, + "node_modules/@blockly/dev-tools/node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@blockly/dev-tools/node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@blockly/dev-tools/node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@blockly/dev-tools/node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@blockly/dev-tools/node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/@blockly/dev-tools/node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/@blockly/dev-tools/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@blockly/eslint-config": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@blockly/eslint-config/-/eslint-config-4.0.1.tgz", @@ -1004,6 +1129,58 @@ "blockly": "^12.0.0" } }, + "node_modules/@blockly/theme-dark": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-dark/-/theme-dark-8.0.1.tgz", + "integrity": "sha512-0Di3WIUwCVQw7jK9myUf/J+4oHLADWc8YxeF40KQgGsyulVrVnYipwtBolj+wxq2xjxIkqgvctAN3BdvM4mynA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, + "node_modules/@blockly/theme-deuteranopia": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-deuteranopia/-/theme-deuteranopia-7.0.1.tgz", + "integrity": "sha512-V05Hk2hzQZict47LfzDdSTP+J5HlYiF7de/8LR/bsRQB/ft7UUTraqDLIivYc9gL2alsVtKzq/yFs9wi7FMAqQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, + "node_modules/@blockly/theme-highcontrast": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-highcontrast/-/theme-highcontrast-7.0.1.tgz", + "integrity": "sha512-dMhysbXf8QtHxuhI1EY5GdZErlfEhjpCogwfzglDKSu8MF2C+5qzOQBxKmqfnEYJl6G9B2HNGw+mEaUo8oel6Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, + "node_modules/@blockly/theme-tritanopia": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@blockly/theme-tritanopia/-/theme-tritanopia-7.0.1.tgz", + "integrity": "sha512-eLqPCmW6xvSYvyTFFE5uz0Bw806LxOmaQrCOzbUywkT41s2ITP06OP1BVQrHdkZSt5whipZYpB1RMGxYxS/Bpw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, "node_modules/@csstools/color-helpers": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", @@ -1530,6 +1707,45 @@ "node": ">=18" } }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", + "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -3996,6 +4212,13 @@ "node": ">=18" } }, + "node_modules/dat.gui": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.9.tgz", + "integrity": "sha512-sCNc1OHobc+Erc1HqiswYgHdVNpSJUlk/Hz8vzOCsER7rl+oF/4+v8GXFUyCgtXpoCX6+bnmg07DedLvBLwYKQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -5659,6 +5882,16 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -6813,6 +7046,13 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true, + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6997,12 +7237,27 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -7353,6 +7608,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/monaco-editor": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.20.0.tgz", + "integrity": "sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7414,6 +7676,37 @@ "node": ">= 0.4.0" } }, + "node_modules/nise": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", + "integrity": "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", + "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "0.0.1" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -9018,6 +9311,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "deprecated": "16.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -9814,6 +10137,16 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", diff --git a/package.json b/package.json index 196a2083..54c23fa0 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ ], "devDependencies": { "@blockly/dev-scripts": "^4.0.8", + "@blockly/dev-tools": "^9.0.3", "@blockly/field-colour": "^6.0.2", "@eslint/eslintrc": "^2.1.2", "@eslint/js": "^8.49.0", diff --git a/test/index.html b/test/index.html index 9eb2a639..7944e004 100644 --- a/test/index.html +++ b/test/index.html @@ -101,6 +101,7 @@ name="scenario" id="scenario" onchange="document.forms.options.submit()"> + @@ -121,27 +122,6 @@ -
- - -
-
- - -
diff --git a/test/index.ts b/test/index.ts index 4aa282a8..c7d2c932 100644 --- a/test/index.ts +++ b/test/index.ts @@ -16,62 +16,41 @@ import {forBlock} from './blocks/p5_generators'; // @ts-expect-error No types in js file import {blocks} from './blocks/p5_blocks'; // @ts-expect-error No types in js file -import {toolbox as toolboxFlyout} from './blocks/toolbox.js'; -// @ts-expect-error No types in js file import toolboxCategories from './toolboxCategories.js'; import {javascriptGenerator} from 'blockly/javascript'; // @ts-expect-error No types in js file import {load} from './loadTestBlocks'; import {runCode, registerRunCodeShortcut} from './runCode'; +import {createPlayground} from '@blockly/dev-tools'; (window as unknown as {Blockly: typeof Blockly}).Blockly = Blockly; /** - * Parse query params for inject and navigation options and update - * the fields on the options form to match. - * - * @returns An options object with keys for each supported option. + * Parse query params for a predefined block scenario and applies it to the + * workspace. */ -function getOptions() { +function applyScenario() { const params = new URLSearchParams(window.location.search); const scenarioParam = params.get('scenario'); - const scenario = scenarioParam ?? 'simpleCircle'; - - const rendererParam = params.get('renderer'); - let renderer = 'zelos'; - // For backwards compatibility with previous behaviour, support - // (e.g.) ?geras as well as ?renderer=geras: - if (rendererParam) { - renderer = rendererParam; - } else if (params.get('geras')) { - renderer = 'geras'; - } else if (params.get('thrasos')) { - renderer = 'thrasos'; - } - - const toolboxParam = params.get('toolbox'); - const toolbox = toolboxParam ?? 'toolbox'; - const toolboxObject = - toolbox === 'flyout' ? toolboxFlyout : toolboxCategories; + const scenario = scenarioParam ?? 'custom'; // Update form inputs to match params, but only after the page is // fully loaded as Chrome (at least) tries to restore previous form // values and does so _after_ DOMContentLoaded has fired, which can // result in the form inputs being out-of-sync with the actual - // options when doing browswer page navigation. + // options when doing browser page navigation. window.addEventListener('load', () => { - (document.getElementById('toolbox') as HTMLSelectElement).value = toolbox; - (document.getElementById('renderer') as HTMLSelectElement).value = renderer; (document.getElementById('scenario') as HTMLSelectElement).value = scenario; }); - return { - scenario, - renderer, - toolbox: toolboxObject, - }; + if (scenario !== 'custom') { + load(Blockly.getMainWorkspace(), scenario); + const url = new URL(window.location.href); + url.searchParams.delete('scenario'); + window.history.replaceState({}, document.title, url.toString()); + } } /** @@ -80,13 +59,8 @@ function getOptions() { * * @returns The created workspace. */ -function createWorkspace(): Blockly.WorkspaceSvg { - const {scenario, renderer, toolbox} = getOptions(); - - const injectOptions = { - toolbox, - renderer, - }; +async function createWorkspace(): Promise { + const injectOptions = {toolbox: toolboxCategories}; const blocklyDiv = document.getElementById('blocklyDiv'); if (!blocklyDiv) { throw new Error('Missing blocklyDiv'); @@ -96,16 +70,30 @@ function createWorkspace(): Blockly.WorkspaceSvg { KeyboardNavigation.registerKeyboardNavigationStyles(); registerFlyoutCursor(); registerNavigationDeferringToolbox(); - const workspace = Blockly.inject(blocklyDiv, injectOptions); - - Blockly.ContextMenuItems.registerCommentOptions(); - new KeyboardNavigation(workspace); registerRunCodeShortcut(); + Blockly.ContextMenuItems.registerCommentOptions(); - // Disable blocks that aren't inside the setup or draw loops. - workspace.addChangeListener(Blockly.Events.disableOrphans); - - load(workspace, scenario); + let navigation: KeyboardNavigation | null = null; + const workspace = ( + await createPlayground( + blocklyDiv, + (blocklyDiv, options) => { + if (navigation) { + navigation.dispose(); + } + const ws = Blockly.inject(blocklyDiv, options); + navigation = new KeyboardNavigation(ws); + + // Disable blocks that aren't inside the setup or draw loops. + ws.addChangeListener(Blockly.Events.disableOrphans); + + return ws; + }, + injectOptions, + ) + ).getWorkspace(); + + applyScenario(); runCode(); return workspace; @@ -124,9 +112,9 @@ function addP5() { javascriptGenerator.addReservedWords('sketch'); } -document.addEventListener('DOMContentLoaded', () => { +document.addEventListener('DOMContentLoaded', async () => { addP5(); - createWorkspace(); + await createWorkspace(); document.getElementById('run')?.addEventListener('click', runCode); // Add Blockly to the global scope so that test code can access it to // verify state after keypresses. From a652b9b1fdf296fe0f4b102087ba57c2a32d174d Mon Sep 17 00:00:00 2001 From: Maribeth Moffatt Date: Mon, 6 Oct 2025 11:40:36 -0700 Subject: [PATCH 3/7] chore: use blockly v12.3.1 for local development (#740) --- package-lock.json | 139 ++-------------------------------------------- package.json | 2 +- 2 files changed, 5 insertions(+), 136 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c066277..0160db2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@types/p5": "^1.7.6", "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", - "blockly": "12.3.0", + "blockly": "12.3.1", "chai": "^5.2.0", "eslint": "^8.49.0", "eslint-config-google": "^0.14.0", @@ -3279,9 +3279,9 @@ } }, "node_modules/blockly": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/blockly/-/blockly-12.3.0.tgz", - "integrity": "sha512-dtxM6Dk8cm0QW4vMJTXmn7xi7a4GnQdXu28Esuuofx7DsYfq73456O5tm3ShUMDcXaFg8w3GVfgoH8I9v6gSVA==", + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/blockly/-/blockly-12.3.1.tgz", + "integrity": "sha512-BbWUcpqroY241XgSxTuAiEMHeIZ6u3+oD2zOATf3Fi+0NMWJ/MdMtuSkOcDCSk6Nc7WR3z5n9GHKqz2L+3kQOQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3838,22 +3838,6 @@ "node": ">=6.0" } }, - "node_modules/chromium-bidi": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.3.tgz", - "integrity": "sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", - "zod": "3.23.8" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -4397,14 +4381,6 @@ "dev": true, "license": "MIT" }, - "node_modules/devtools-protocol": { - "version": "0.0.1312386", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", - "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/diff": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", @@ -7493,14 +7469,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/mocha": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", @@ -8460,48 +8428,6 @@ "node": ">=6" } }, - "node_modules/puppeteer-core": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.15.0.tgz", - "integrity": "sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@puppeteer/browsers": "2.3.0", - "chromium-bidi": "0.6.3", - "debug": "^4.3.6", - "devtools-protocol": "0.0.1312386", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz", - "integrity": "sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "debug": "^4.3.5", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.4.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -9967,14 +9893,6 @@ "dev": true, "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -10188,44 +10106,6 @@ "node": ">=14.17" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "node_modules/unbzip2-stream/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "optional": true, - "peer": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/undici": { "version": "6.21.2", "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.2.tgz", @@ -11248,17 +11128,6 @@ "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } - }, - "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "dev": true, - "optional": true, - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/package.json b/package.json index 54c23fa0..bce40631 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@types/p5": "^1.7.6", "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", - "blockly": "12.3.0", + "blockly": "12.3.1", "chai": "^5.2.0", "eslint": "^8.49.0", "eslint-config-google": "^0.14.0", From c0a3a81caf13905a466d84454ae13553faf588c9 Mon Sep 17 00:00:00 2001 From: Maribeth Moffatt Date: Mon, 6 Oct 2025 15:47:02 -0700 Subject: [PATCH 4/7] release: bump version to 3.0.3 (#752) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0160db2b..dcdb8356 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@blockly/keyboard-navigation", - "version": "3.0.2", + "version": "3.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@blockly/keyboard-navigation", - "version": "3.0.2", + "version": "3.0.3", "license": "Apache-2.0", "devDependencies": { "@blockly/dev-scripts": "^4.0.8", diff --git a/package.json b/package.json index bce40631..663b312d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@blockly/keyboard-navigation", - "version": "3.0.2", + "version": "3.0.3", "description": "A plugin for keyboard navigation.", "scripts": { "audit:fix": "blockly-scripts auditFix", From fd27e911f7bad58c7b4e54e74a3d455a4fcd61e0 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Fri, 17 Oct 2025 13:37:01 -0700 Subject: [PATCH 5/7] fix: Fix stack navigation with immovable blocks. (#761) --- src/actions/stack_navigation.ts | 54 +++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/actions/stack_navigation.ts b/src/actions/stack_navigation.ts index 8d648141..de1ae3c3 100644 --- a/src/actions/stack_navigation.ts +++ b/src/actions/stack_navigation.ts @@ -4,7 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -import {ShortcutRegistry, WorkspaceSvg, utils} from 'blockly/core'; +import { + ShortcutRegistry, + WorkspaceSvg, + BlockSvg, + navigateStacks, + isSelectable, + utils, +} from 'blockly/core'; import * as Constants from '../constants'; /** @@ -16,26 +23,13 @@ export class StackNavigationAction { install() { const preconditionFn = (workspace: WorkspaceSvg) => - !!getCurNodeRoot(workspace); - - function getCurNodeRoot(workspace: WorkspaceSvg) { - const cursor = workspace.getCursor(); - // The fallback case includes workspace comments. - return cursor.getSourceBlock()?.getRootBlock() ?? cursor.getCurNode(); - } + !!this.getCurNodeRoot(workspace); const previousStackShortcut: ShortcutRegistry.KeyboardShortcut = { name: Constants.SHORTCUT_NAMES.PREVIOUS_STACK, preconditionFn, callback: (workspace) => { - const curNodeRoot = getCurNodeRoot(workspace); - if (!curNodeRoot) return false; - const prevRoot = workspace - .getNavigator() - .getPreviousSibling(curNodeRoot); - if (!prevRoot) return false; - workspace.getCursor().setCurNode(prevRoot); - return true; + return this.navigate(workspace, -1); }, keyCodes: [utils.KeyCodes.B], }; @@ -44,12 +38,7 @@ export class StackNavigationAction { name: Constants.SHORTCUT_NAMES.NEXT_STACK, preconditionFn, callback: (workspace) => { - const curNodeRoot = getCurNodeRoot(workspace); - if (!curNodeRoot) return false; - const nextRoot = workspace.getNavigator().getNextSibling(curNodeRoot); - if (!nextRoot) return false; - workspace.getCursor().setCurNode(nextRoot); - return true; + return this.navigate(workspace, 1); }, keyCodes: [utils.KeyCodes.N], }; @@ -60,6 +49,27 @@ export class StackNavigationAction { this.stackShortcuts.push(nextStackShortcut); } + private getCurNodeRoot(workspace: WorkspaceSvg) { + const cursor = workspace.getCursor(); + // The fallback case includes workspace comments. + return cursor.getSourceBlock()?.getRootBlock() ?? cursor.getCurNode(); + } + + private navigate(workspace: WorkspaceSvg, delta: number) { + const curNodeRoot = this.getCurNodeRoot(workspace); + if (!curNodeRoot || !isSelectable(curNodeRoot)) { + return false; + } + + let nextRoot = navigateStacks(curNodeRoot, delta); + if (!nextRoot) return false; + if (nextRoot instanceof BlockSvg) { + nextRoot = nextRoot.getRootBlock(); + } + workspace.getCursor().setCurNode(nextRoot); + return true; + } + /** * Unregisters the shortcut. */ From 28e2582c9ca5a3e8e0ff03bf02e47753f28e57bb Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Thu, 20 Nov 2025 15:59:30 -0800 Subject: [PATCH 6/7] fix: Fix failing NPM build. (#769) NPM builds will be failing in CI due to the same reason this has happened in core Blocky: https://github.com/RaspberryPiFoundation/blockly/pull/9476. --- package-lock.json | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index dcdb8356..2708e624 100644 --- a/package-lock.json +++ b/package-lock.json @@ -947,6 +947,7 @@ "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -1265,6 +1266,7 @@ "url": "https://opencollective.com/csstools" } ], + "peer": true, "engines": { "node": ">=18" }, @@ -1287,6 +1289,7 @@ "url": "https://opencollective.com/csstools" } ], + "peer": true, "engines": { "node": ">=18" } @@ -2084,6 +2087,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -2766,6 +2770,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2799,6 +2804,7 @@ "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", @@ -3284,6 +3290,7 @@ "integrity": "sha512-BbWUcpqroY241XgSxTuAiEMHeIZ6u3+oD2zOATf3Fi+0NMWJ/MdMtuSkOcDCSk6Nc7WR3z5n9GHKqz2L+3kQOQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "jsdom": "26.1.0" }, @@ -3414,6 +3421,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -4852,6 +4860,7 @@ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5665,6 +5674,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -5773,6 +5783,21 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -6870,6 +6895,7 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, + "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -9386,6 +9412,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -9799,6 +9826,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -10098,6 +10126,7 @@ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10439,6 +10468,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -10486,6 +10516,7 @@ "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -10678,7 +10709,8 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/webpack-dev-server": { "version": "5.2.1", @@ -10769,6 +10801,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", From d735e06c5afcaa1fb8dbcef0df5a4c5eea6cfac9 Mon Sep 17 00:00:00 2001 From: kmcnaught <12151404+kmcnaught@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:11:13 +0000 Subject: [PATCH 7/7] Update test page link in README.md (#771) To point at rasp pi deployment. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54dc1aae..d8871d02 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ For more planning and timeline information please see our ## End-user instructions -You can explore the current state of the plugin on the [test page](https://google.github.io/blockly-keyboard-experimentation/). +You can explore the current state of the plugin on the [test page](https://raspberrypifoundation.github.io/blockly-keyboard-experimentation/). To use keyboard navigation, click on the workspace or press tab until you reach the workspace.