From b6282096429c9a4ef9c53228097cd744457f1065 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 09:04:29 -0800 Subject: [PATCH 01/31] chore(deps): Bump webdriverio from 8.24.6 to 8.27.0 (#7737) Bumps [webdriverio](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio) from 8.24.6 to 8.27.0. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v8.27.0/packages/webdriverio) --- updated-dependencies: - dependency-name: webdriverio dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 180 ++++++++++++++++++---------------------------- 1 file changed, 71 insertions(+), 109 deletions(-) diff --git a/package-lock.json b/package-lock.json index a857b8c04..906aa02d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1360,18 +1360,18 @@ "dev": true }, "node_modules/@wdio/config": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.24.6.tgz", - "integrity": "sha512-ZFmd6rB1kgL4k/SjLXbtFTCxvxSf1qzdt/losiTqkqFBYznkTRUBGSoGaVTlkMtHAReiVSK92sICc15JWaCdEA==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.27.0.tgz", + "integrity": "sha512-zYM5daeiBVVAbQj0ASymAt0RUsocLVIwKiUHNa8gg/1GsZnztGjetXExSp1gXlxtMVM5xWUSKjh6ceFK79gWDQ==", "dev": true, "dependencies": { - "@wdio/logger": "8.16.17", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", - "import-meta-resolve": "^3.0.0" + "import-meta-resolve": "^4.0.0" }, "engines": { "node": "^16.13 || >=18" @@ -1390,9 +1390,9 @@ } }, "node_modules/@wdio/logger": { - "version": "8.16.17", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.16.17.tgz", - "integrity": "sha512-zeQ41z3T+b4IsrriZZipayXxLNDuGsm7TdExaviNGojPVrIsQUCSd/FvlLHM32b7ZrMyInHenu/zx1cjAZO71g==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.24.12.tgz", + "integrity": "sha512-QisOiVIWKTUCf1H7S+DOtC+gruhlpimQrUXfWMTeeh672PvAJYnTpOJDWA+BtXfsikkUYFAzAaq8SeMJk8rqKg==", "dev": true, "dependencies": { "chalk": "^5.1.2", @@ -1444,15 +1444,15 @@ } }, "node_modules/@wdio/protocols": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.23.0.tgz", - "integrity": "sha512-2XTzD+lqQP3g8BWn+Bn5BTFzjHqzZNwq7DjlYrb27Bq8nOA+1DEcj3WzQ6V6CktTnKI/LAYKA1IFAF//Azrp/Q==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.24.12.tgz", + "integrity": "sha512-QnVj3FkapmVD3h2zoZk+ZQ8gevSj9D9MiIQIy8eOnY4FAneYZ9R9GvoW+mgNcCZO8S8++S/jZHetR8n+8Q808g==", "dev": true }, "node_modules/@wdio/repl": { - "version": "8.23.1", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.23.1.tgz", - "integrity": "sha512-u6zG2cgBm67V5/WlQzadWqLGXs3moH8MOsgoljULQncelSBBZGZ5DyLB4p7jKcUAsKtMjgmFQmIvpQoqmyvdfg==", + "version": "8.24.12", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.24.12.tgz", + "integrity": "sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==", "dev": true, "dependencies": { "@types/node": "^20.1.0" @@ -1462,9 +1462,9 @@ } }, "node_modules/@wdio/types": { - "version": "8.24.2", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.24.2.tgz", - "integrity": "sha512-x7iWF5NM8NfVxziGwLdQ+3sstgSxRoqfmmFEDTDps0oFrN5CgkqcoLkqXJ5u166gvpxpEq0gxZwxkbPC/Lp0cw==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.27.0.tgz", + "integrity": "sha512-LbP9FKh8r0uW9/dKhTIUCC1Su8PsP9TmzGKXkWt6/IMacgJiB/zW3u1CgyaLw9lG0UiQORHGoeJX9zB2HZAh4w==", "dev": true, "dependencies": { "@types/node": "^20.1.0" @@ -1474,21 +1474,20 @@ } }, "node_modules/@wdio/utils": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.24.6.tgz", - "integrity": "sha512-qwcshLH9iKnhK0jXoXjPw3G02UhyShT0I+ljC0hMybJEBsra92TYFa47Cp6n1fdvM3+/BTuhsgtzRz0anObicQ==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.27.0.tgz", + "integrity": "sha512-4BY+JBQssVn003P5lA289uDMie3LtGinHze5btkcW9timB6VaU+EeZS4eKTPC0pziizLhteVvXYxv3YTpeeRfA==", "dev": true, "dependencies": { "@puppeteer/browsers": "^1.6.0", - "@wdio/logger": "8.16.17", - "@wdio/types": "8.24.2", + "@wdio/logger": "8.24.12", + "@wdio/types": "8.27.0", "decamelize": "^6.0.0", "deepmerge-ts": "^5.1.0", "edgedriver": "^5.3.5", "geckodriver": "^4.2.0", "get-port": "^7.0.0", - "got": "^13.0.0", - "import-meta-resolve": "^3.0.0", + "import-meta-resolve": "^4.0.0", "locate-app": "^2.1.0", "safaridriver": "^0.1.0", "split2": "^4.2.0", @@ -1499,9 +1498,9 @@ } }, "node_modules/@wdio/utils/node_modules/@puppeteer/browsers": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.8.0.tgz", - "integrity": "sha512-TkRHIV6k2D8OlUe8RtG+5jgOF/H98Myx0M6AOafC8DdNVOFiBSFa5cpRDtpm8LXOa9sVwe0+e6Q3FC56X/DZfg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.0.tgz", + "integrity": "sha512-QwguOLy44YBGC8vuPP2nmpX4MUN2FzWbsnvZJtiCzecU3lHmVZkaC1tq6rToi9a200m8RzlVtDyxCS0UIDrxUg==", "dev": true, "dependencies": { "debug": "4.3.4", @@ -3340,9 +3339,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1213968", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1213968.tgz", - "integrity": "sha512-o4n/beY+3CcZwFctYapjGelKptR4AuQT5gXS1Kvgbig+ArwkxK7f8wDVuD1wsoswiJWCwV6OK+Qb7vhNzNmABQ==", + "version": "0.0.1237913", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1237913.tgz", + "integrity": "sha512-Pxtmz2ZIqBkpU82HaIdsvCQBG94yTC4xajrEsWx9p38QKEfBCJktSazsHkrjf9j3dVVNPhg5LR21F6KWeXpjiQ==", "dev": true }, "node_modules/dir-glob": { @@ -3485,9 +3484,9 @@ } }, "node_modules/edgedriver": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.3.8.tgz", - "integrity": "sha512-FWLPDuwJDeGGgtmlqTXb4lQi/HV9yylLo1F9O1g9TLqSemA5T6xH28seUIfyleVirLFtDQyKNUxKsMhMT4IfnA==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-5.3.9.tgz", + "integrity": "sha512-G0wNgFMFRDnFfKaXG2R6HiyVHqhKwdQ3EgoxW3wPlns2wKqem7F+HgkWBcevN7Vz0nN4AXtskID7/6jsYDXcKw==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -4722,17 +4721,17 @@ "dev": true }, "node_modules/geckodriver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", - "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.3.0.tgz", + "integrity": "sha512-QfpvxFsMORwKpvnLslkHCr3NTCczHAvkte6+pQGsiUZXKBe6mO4TTb727b+9KMVSK6XZqhR6ZwImKdP+F5vS6A==", "dev": true, "hasInstallScript": true, "dependencies": { - "@wdio/logger": "^8.11.0", + "@wdio/logger": "^8.24.12", "decamelize": "^6.0.0", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.1", + "https-proxy-agent": "^7.0.2", + "node-fetch": "^3.3.2", "tar-fs": "^3.0.4", "unzipper": "^0.10.14", "which": "^4.0.0" @@ -5491,9 +5490,9 @@ ] }, "node_modules/got": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", - "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", "dev": true, "dependencies": { "@sindresorhus/is": "^5.2.0", @@ -5509,7 +5508,7 @@ "responselike": "^3.0.0" }, "engines": { - "node": ">=16" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sindresorhus/got?sponsor=1" @@ -6395,9 +6394,9 @@ } }, "node_modules/import-meta-resolve": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz", - "integrity": "sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", "dev": true, "funding": { "type": "github", @@ -9591,9 +9590,9 @@ "dev": true }, "node_modules/safaridriver": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.0.tgz", - "integrity": "sha512-azzzIP3gR1TB9bVPv7QO4Zjw0rR1BWEU/s2aFdUMN48gxDjxEB13grAEuXDmkKPgE74cObymDxmAmZnL3clj4w==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.1.tgz", + "integrity": "sha512-dpCmh2EYKh9G61nR+ve0w2+WW2YJX59Rtke0pUoUXbvGKPDLK+NcL7I3VBS1UcGJbA6ptQTT82JcGwJHALD0kQ==", "dev": true }, "node_modules/safe-buffer": { @@ -11311,20 +11310,20 @@ } }, "node_modules/webdriver": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.24.6.tgz", - "integrity": "sha512-k5XI2/SHd/14h4ElPQH8EzSUXujZIGbBEi+3dTS2H457KFR5Q8QYfIazDs/YnEdooOp8b6Oe9N7qI99LP8K6bQ==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.27.0.tgz", + "integrity": "sha512-n1IA+rR3u84XxU9swiKUM06BkEC0GDimfZkBML57cny+utQOUbdM/mBpqCUnkWX/RBz/p2EfHdKNyOs3/REaog==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.24.6", - "@wdio/logger": "8.16.17", - "@wdio/protocols": "8.23.0", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/config": "8.27.0", + "@wdio/logger": "8.24.12", + "@wdio/protocols": "8.24.12", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "deepmerge-ts": "^5.1.0", - "got": "^ 12.6.1", + "got": "^12.6.1", "ky": "^0.33.0", "ws": "^8.8.0" }, @@ -11332,63 +11331,26 @@ "node": "^16.13 || >=18" } }, - "node_modules/webdriver/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webdriver/node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, "node_modules/webdriverio": { - "version": "8.24.6", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.24.6.tgz", - "integrity": "sha512-gJMAJiErbXe/oFJbV+H9lXp9GPxnUgHrbtxkG6SCKQlk1zPFho9FZ3fQWl/ty84w5n9ZMhAdnQIfZM9aytxIBQ==", + "version": "8.27.0", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.27.0.tgz", + "integrity": "sha512-Qh5VCiBjEmxnmXcL1QEFoDzFqTtaWKrXriuU5G0yHKCModGAt2G7IHTkAok3CpmkVJfZpEvY630aP1MvgDtFhw==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.24.6", - "@wdio/logger": "8.16.17", - "@wdio/protocols": "8.23.0", - "@wdio/repl": "8.23.1", - "@wdio/types": "8.24.2", - "@wdio/utils": "8.24.6", + "@wdio/config": "8.27.0", + "@wdio/logger": "8.24.12", + "@wdio/protocols": "8.24.12", + "@wdio/repl": "8.24.12", + "@wdio/types": "8.27.0", + "@wdio/utils": "8.27.0", "archiver": "^6.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools-protocol": "^0.0.1213968", + "devtools-protocol": "^0.0.1237913", "grapheme-splitter": "^1.0.2", - "import-meta-resolve": "^3.0.0", + "import-meta-resolve": "^4.0.0", "is-plain-obj": "^4.1.0", "lodash.clonedeep": "^4.5.0", "lodash.zip": "^4.2.0", @@ -11398,7 +11360,7 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.24.6" + "webdriver": "8.27.0" }, "engines": { "node": "^16.13 || >=18" From 811a33cd9a14be62181a3a63dae642d094f32949 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:30:23 -0800 Subject: [PATCH 02/31] chore(deps): Bump prettier from 3.1.0 to 3.1.1 (#7736) Bumps [prettier](https://github.com/prettier/prettier) from 3.1.0 to 3.1.1. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.1.0...3.1.1) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 906aa02d1..73f26d67e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,7 +45,7 @@ "markdown-tables-to-json": "^0.1.7", "mocha": "^10.0.0", "patch-package": "^8.0.0", - "prettier": "3.1.0", + "prettier": "3.1.1", "readline-sync": "^1.4.10", "rimraf": "^5.0.0", "typescript": "^5.0.2", @@ -8916,9 +8916,9 @@ } }, "node_modules/prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" diff --git a/package.json b/package.json index aaf3fad2a..c3653ea68 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "markdown-tables-to-json": "^0.1.7", "mocha": "^10.0.0", "patch-package": "^8.0.0", - "prettier": "3.1.0", + "prettier": "3.1.1", "readline-sync": "^1.4.10", "rimraf": "^5.0.0", "typescript": "^5.0.2", From a5fafd752bb80c1e6ff58b229e7427677024d450 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:30:33 -0800 Subject: [PATCH 03/31] chore(deps): Bump @blockly/theme-modern from 5.0.4 to 5.0.5 (#7735) Bumps [@blockly/theme-modern](https://github.com/google/blockly-samples/tree/HEAD/plugins/theme-modern) from 5.0.4 to 5.0.5. - [Release notes](https://github.com/google/blockly-samples/releases) - [Changelog](https://github.com/google/blockly-samples/blob/master/plugins/theme-modern/CHANGELOG.md) - [Commits](https://github.com/google/blockly-samples/commits/@blockly/theme-modern@5.0.5/plugins/theme-modern) --- updated-dependencies: - dependency-name: "@blockly/theme-modern" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 73f26d67e..93062d5ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -148,9 +148,9 @@ } }, "node_modules/@blockly/theme-modern": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@blockly/theme-modern/-/theme-modern-5.0.4.tgz", - "integrity": "sha512-AamkRgc5XDvENdEBol8GVUebBooAaHP/yGfixVQ3Oj48xErKisUbLuCpZ4emvahewGghJ55HVXSJKdLQ2n0h8w==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@blockly/theme-modern/-/theme-modern-5.0.5.tgz", + "integrity": "sha512-rbVOGxKHAatzHI6Yhy9lJbIRPzAW2Xgf+N1U1KSkyVmUziLKKaNKwwYvnOSx4MmoDD49SrZMdUgT8G+VBLFhYw==", "dev": true, "engines": { "node": ">=8.17.0" From 2ebc6e14bcab2bce9a26916de898394eef10f016 Mon Sep 17 00:00:00 2001 From: Ebrahim Haji <14169174+ebrahim95@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:20:41 -0600 Subject: [PATCH 04/31] fix: prevent console logging duplicate deprecation warnings (#7733) * (feat): added Set to prevent console logging multiple deprecation warnings #7719 * feat!: added Set to prevent console logging multiple deprecation warnings #7719 * refactor: renamed variable and rewrote comment Edited By Cpcallen Co-authored-by: Christopher Allen * refactor: added guard clause and rewrote comment Edited By Cpcallen Co-authored-by: Christopher Allen * refactor: removed checkMsg Variable name and replaced it with previousWarnings * refactor: removed checkMsg Variable name and replaced it with previousWarnings --------- Co-authored-by: Christopher Allen --- core/utils/deprecation.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/utils/deprecation.ts b/core/utils/deprecation.ts index da7977c3e..c793b5f57 100644 --- a/core/utils/deprecation.ts +++ b/core/utils/deprecation.ts @@ -6,6 +6,9 @@ // Former goog.module ID: Blockly.utils.deprecation +// Set of previously-emitted warnings. +const previousWarnings = new Set(); + /** * Warn developers that a function or property is deprecated. * @@ -33,5 +36,12 @@ export function warn( if (opt_use) { msg += '\nUse ' + opt_use + ' instead.'; } + + // Don't log deprecation warnings multiple times. + if (previousWarnings.has(msg)) { + return; + } + + previousWarnings.add(msg); console.warn(msg); } From a3b16cd0c9a8459510f3dc6a8dde44b038f037a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:22:35 +0000 Subject: [PATCH 05/31] chore(deps): Bump eslint-plugin-jsdoc from 46.9.0 to 48.0.2 (#7751) Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 46.9.0 to 48.0.2. - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v46.9.0...v48.0.2) --- updated-dependencies: - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 24 +++++++++++++++++------- package.json | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 93062d5ff..845ab4c27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "eslint": "^8.4.1", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^9.0.0", - "eslint-plugin-jsdoc": "^46.2.6", + "eslint-plugin-jsdoc": "^48.0.2", "glob": "^10.3.4", "google-closure-compiler": "^20230802.0.0", "gulp": "^4.0.2", @@ -3775,9 +3775,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "46.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.9.0.tgz", - "integrity": "sha512-UQuEtbqLNkPf5Nr/6PPRCtr9xypXY+g8y/Q7gPa0YK7eDhh0y2lWprXRnaYbW7ACgIUvpDKy9X2bZqxtGzBG9Q==", + "version": "48.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.0.2.tgz", + "integrity": "sha512-CBFl5Jc7+jlV36RwDm+PQ8Uw5r28pn2/uW/OaB+Gw5bFwn4Py/1eYMZ3hGf9S4meUFZ/sRvS+hVif2mRAp6WqQ==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.41.0", @@ -3788,13 +3788,23 @@ "esquery": "^1.5.0", "is-builtin-module": "^3.2.1", "semver": "^7.5.4", - "spdx-expression-parse": "^3.0.1" + "spdx-expression-parse": "^4.0.0" }, "engines": { - "node": ">=16" + "node": ">=18" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, "node_modules/eslint-scope": { diff --git a/package.json b/package.json index c3653ea68..59410a930 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "eslint": "^8.4.1", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^9.0.0", - "eslint-plugin-jsdoc": "^46.2.6", + "eslint-plugin-jsdoc": "^48.0.2", "glob": "^10.3.4", "google-closure-compiler": "^20230802.0.0", "gulp": "^4.0.2", From 187e4cc3d7f932f571028f680674103359c1d257 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 11:31:28 +0000 Subject: [PATCH 06/31] chore(deps-dev): Bump follow-redirects from 1.15.2 to 1.15.4 (#7778) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 845ab4c27..4276a439f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4499,9 +4499,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "dev": true, "funding": [ { From 7243b48d47f35f6669f243e16711baa8173a6580 Mon Sep 17 00:00:00 2001 From: truongductri01 <58579187+truongductri01@users.noreply.github.com> Date: Thu, 11 Jan 2024 23:35:09 +0700 Subject: [PATCH 07/31] feat: add muted option (#7714) * feat: add muted option * fix: linter * Update core/workspace_audio.ts Co-authored-by: Beka Westberg * Update core/workspace_audio.ts Co-authored-by: Beka Westberg * Update core/workspace_audio.ts Co-authored-by: Beka Westberg * Update core/workspace_audio.ts Co-authored-by: Beka Westberg --------- Co-authored-by: Beka Westberg --- core/workspace_audio.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/workspace_audio.ts b/core/workspace_audio.ts index 9f4dc54fe..c2ab93222 100644 --- a/core/workspace_audio.ts +++ b/core/workspace_audio.ts @@ -31,6 +31,9 @@ export class WorkspaceAudio { /** Time that the last sound was played. */ private lastSound_: Date | null = null; + /** Whether the audio is muted or not. */ + private muted: boolean = false; + /** * @param parentWorkspace The parent of the workspace this audio object * belongs to, or null. @@ -121,6 +124,9 @@ export class WorkspaceAudio { * @param opt_volume Volume of sound (0-1). */ play(name: string, opt_volume?: number) { + if (this.muted) { + return; + } const sound = this.sounds.get(name); if (sound) { // Don't play one sound on top of another. @@ -148,4 +154,18 @@ export class WorkspaceAudio { this.parentWorkspace.getAudioManager().play(name, opt_volume); } } + + /** + * @param muted If true, mute sounds. Otherwise, play them. + */ + setMuted(muted: boolean) { + this.muted = muted; + } + + /** + * @returns Whether the audio is currently muted or not. + */ + getMuted(): boolean { + return this.muted; + } } From 5ade042e9596cfb155d080c8cc4f375bc787b078 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 12 Jan 2024 13:43:55 -0800 Subject: [PATCH 08/31] fix: triggering flyout show from field render causing infinite loop (#7784) * fix: triggering flyout show from field render causing infinite loop * chore: add tests for triggering queued renders --- core/flyout_base.ts | 2 +- core/render_management.ts | 21 ++++++--- tests/mocha/render_management_test.js | 65 +++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/core/flyout_base.ts b/core/flyout_base.ts index 52d9a2298..4b022c535 100644 --- a/core/flyout_base.ts +++ b/core/flyout_base.ts @@ -649,7 +649,7 @@ export abstract class Flyout const parsedContent = toolbox.convertFlyoutDefToJsonArray(flyoutDef); const flyoutInfo = this.createFlyoutInfo(parsedContent); - renderManagement.triggerQueuedRenders(); + renderManagement.triggerQueuedRenders(this.workspace_); this.layout_(flyoutInfo.contents, flyoutInfo.gaps); diff --git a/core/render_management.ts b/core/render_management.ts index 541459860..d3f1ed4f1 100644 --- a/core/render_management.ts +++ b/core/render_management.ts @@ -6,6 +6,7 @@ import {BlockSvg} from './block_svg.js'; import * as userAgent from './utils/useragent.js'; +import type {WorkspaceSvg} from './workspace_svg.js'; /** The set of all blocks in need of rendering which don't have parents. */ const rootBlocks = new Set(); @@ -75,11 +76,13 @@ export function finishQueuedRenders(): Promise { * cases where queueing renders breaks functionality + backwards compatibility * (such as rendering icons). * + * @param workspace If provided, only rerender blocks in this workspace. + * * @internal */ -export function triggerQueuedRenders() { +export function triggerQueuedRenders(workspace?: WorkspaceSvg) { window.cancelAnimationFrame(animationRequestId); - doRenders(); + doRenders(workspace); if (afterRendersResolver) afterRendersResolver(); } @@ -110,10 +113,16 @@ function queueBlock(block: BlockSvg) { /** * Rerenders all of the blocks in the queue. + * + * @param workspace If provided, only rerender blocks in this workspace. */ -function doRenders() { - const workspaces = new Set([...rootBlocks].map((block) => block.workspace)); - const blocks = [...rootBlocks].filter(shouldRenderRootBlock); +function doRenders(workspace?: WorkspaceSvg) { + const workspaces = workspace + ? new Set([workspace]) + : new Set([...rootBlocks].map((block) => block.workspace)); + const blocks = [...rootBlocks] + .filter(shouldRenderRootBlock) + .filter((b) => workspaces.has(b.workspace)); for (const block of blocks) { renderBlock(block); } @@ -126,7 +135,7 @@ function doRenders() { } rootBlocks.clear(); - dirtyBlocks = new Set(); + dirtyBlocks = new WeakSet(); afterRendersPromise = null; } diff --git a/tests/mocha/render_management_test.js b/tests/mocha/render_management_test.js index d5d957df4..4a69d2bab 100644 --- a/tests/mocha/render_management_test.js +++ b/tests/mocha/render_management_test.js @@ -57,4 +57,69 @@ suite('Render Management', function () { return promise; }); }); + + suite('triggering queued renders', function () { + function createMockBlock(ws) { + return { + hasRendered: false, + renderEfficiently: function () { + this.hasRendered = true; + }, + + // All of the APIs the render management system needs. + getParent: () => null, + getChildren: () => [], + isDisposed: () => false, + getRelativeToSurfaceXY: () => ({x: 0, y: 0}), + updateComponentLocations: () => {}, + workspace: ws || createMockWorkspace(), + }; + } + + function createMockWorkspace() { + return { + resizeContents: () => {}, + }; + } + + test('triggering queued renders rerenders blocks', function () { + const block = createMockBlock(); + Blockly.renderManagement.queueRender(block); + + Blockly.renderManagement.triggerQueuedRenders(); + + chai.assert.isTrue(block.hasRendered, 'Expected block to be rendered'); + }); + + test('triggering queued renders rerenders blocks in all workspaces', function () { + const workspace1 = createMockWorkspace(); + const workspace2 = createMockWorkspace(); + const block1 = createMockBlock(workspace1); + const block2 = createMockBlock(workspace2); + Blockly.renderManagement.queueRender(block1); + Blockly.renderManagement.queueRender(block2); + + Blockly.renderManagement.triggerQueuedRenders(); + + chai.assert.isTrue(block1.hasRendered, 'Expected block1 to be rendered'); + chai.assert.isTrue(block2.hasRendered, 'Expected block2 to be rendered'); + }); + + test('triggering queued renders in one workspace does not rerender blocks in another workspace', function () { + const workspace1 = createMockWorkspace(); + const workspace2 = createMockWorkspace(); + const block1 = createMockBlock(workspace1); + const block2 = createMockBlock(workspace2); + Blockly.renderManagement.queueRender(block1); + Blockly.renderManagement.queueRender(block2); + + Blockly.renderManagement.triggerQueuedRenders(workspace1); + + chai.assert.isTrue(block1.hasRendered, 'Expected block1 to be rendered'); + chai.assert.isFalse( + block2.hasRendered, + 'Expected block2 to not be rendered', + ); + }); + }); }); From 0d1245c6469667e9acc19887c3f47eea85c3bcbc Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Tue, 16 Jan 2024 14:23:58 -0800 Subject: [PATCH 09/31] fix: cancelling all renders on triggering queued renders (#7787) --- core/render_management.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/core/render_management.ts b/core/render_management.ts index d3f1ed4f1..f0ec2a133 100644 --- a/core/render_management.ts +++ b/core/render_management.ts @@ -12,7 +12,7 @@ import type {WorkspaceSvg} from './workspace_svg.js'; const rootBlocks = new Set(); /** The set of all blocks in need of rendering. */ -let dirtyBlocks = new WeakSet(); +const dirtyBlocks = new WeakSet(); /** * The promise which resolves after the current set of renders is completed. Or @@ -81,9 +81,9 @@ export function finishQueuedRenders(): Promise { * @internal */ export function triggerQueuedRenders(workspace?: WorkspaceSvg) { - window.cancelAnimationFrame(animationRequestId); + if (!workspace) window.cancelAnimationFrame(animationRequestId); doRenders(workspace); - if (afterRendersResolver) afterRendersResolver(); + if (!workspace && afterRendersResolver) afterRendersResolver(); } /** @@ -134,9 +134,19 @@ function doRenders(workspace?: WorkspaceSvg) { block.updateComponentLocations(blockOrigin); } - rootBlocks.clear(); - dirtyBlocks = new WeakSet(); - afterRendersPromise = null; + for (const block of blocks) { + dequeueBlock(block); + } + if (!workspace) afterRendersPromise = null; +} + +/** Removes the given block and children from the render queue. */ +function dequeueBlock(block: BlockSvg) { + rootBlocks.delete(block); + dirtyBlocks.delete(block); + for (const child of block.getChildren(false)) { + dequeueBlock(child); + } } /** From 49f65a235c361cbb931042ed9bb6da4c7c94b82a Mon Sep 17 00:00:00 2001 From: Maribeth Bottorff Date: Wed, 17 Jan 2024 12:05:49 -0800 Subject: [PATCH 10/31] fix: first block dragged from flyout will have same id (#7788) --- core/flyout_base.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/flyout_base.ts b/core/flyout_base.ts index 4b022c535..438e92d64 100644 --- a/core/flyout_base.ts +++ b/core/flyout_base.ts @@ -1251,8 +1251,7 @@ export abstract class Flyout } // Clone the block. - // TODO(#7432): Add a saveIds parameter to `save`. - const json = blocks.save(oldBlock, {saveIds: false}) as blocks.State; + const json = blocks.save(oldBlock) as blocks.State; // Normallly this resizes leading to weird jumps. Save it for terminateDrag. targetWorkspace.setResizesEnabled(false); const block = blocks.append(json, targetWorkspace) as BlockSvg; From f027b827f0f788746a08169c9e77f8225b1166f8 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 12 Jan 2024 13:43:55 -0800 Subject: [PATCH 11/31] fix: triggering flyout show from field render causing infinite loop (#7784) * fix: triggering flyout show from field render causing infinite loop * chore: add tests for triggering queued renders (cherry picked from commit 5ade042e9596cfb155d080c8cc4f375bc787b078) --- core/flyout_base.ts | 2 +- core/render_management.ts | 21 ++++++--- tests/mocha/render_management_test.js | 65 +++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/core/flyout_base.ts b/core/flyout_base.ts index 76d2e8f8c..1ee1a55d5 100644 --- a/core/flyout_base.ts +++ b/core/flyout_base.ts @@ -649,7 +649,7 @@ export abstract class Flyout const parsedContent = toolbox.convertFlyoutDefToJsonArray(flyoutDef); const flyoutInfo = this.createFlyoutInfo(parsedContent); - renderManagement.triggerQueuedRenders(); + renderManagement.triggerQueuedRenders(this.workspace_); this.layout_(flyoutInfo.contents, flyoutInfo.gaps); diff --git a/core/render_management.ts b/core/render_management.ts index 541459860..d3f1ed4f1 100644 --- a/core/render_management.ts +++ b/core/render_management.ts @@ -6,6 +6,7 @@ import {BlockSvg} from './block_svg.js'; import * as userAgent from './utils/useragent.js'; +import type {WorkspaceSvg} from './workspace_svg.js'; /** The set of all blocks in need of rendering which don't have parents. */ const rootBlocks = new Set(); @@ -75,11 +76,13 @@ export function finishQueuedRenders(): Promise { * cases where queueing renders breaks functionality + backwards compatibility * (such as rendering icons). * + * @param workspace If provided, only rerender blocks in this workspace. + * * @internal */ -export function triggerQueuedRenders() { +export function triggerQueuedRenders(workspace?: WorkspaceSvg) { window.cancelAnimationFrame(animationRequestId); - doRenders(); + doRenders(workspace); if (afterRendersResolver) afterRendersResolver(); } @@ -110,10 +113,16 @@ function queueBlock(block: BlockSvg) { /** * Rerenders all of the blocks in the queue. + * + * @param workspace If provided, only rerender blocks in this workspace. */ -function doRenders() { - const workspaces = new Set([...rootBlocks].map((block) => block.workspace)); - const blocks = [...rootBlocks].filter(shouldRenderRootBlock); +function doRenders(workspace?: WorkspaceSvg) { + const workspaces = workspace + ? new Set([workspace]) + : new Set([...rootBlocks].map((block) => block.workspace)); + const blocks = [...rootBlocks] + .filter(shouldRenderRootBlock) + .filter((b) => workspaces.has(b.workspace)); for (const block of blocks) { renderBlock(block); } @@ -126,7 +135,7 @@ function doRenders() { } rootBlocks.clear(); - dirtyBlocks = new Set(); + dirtyBlocks = new WeakSet(); afterRendersPromise = null; } diff --git a/tests/mocha/render_management_test.js b/tests/mocha/render_management_test.js index d5d957df4..4a69d2bab 100644 --- a/tests/mocha/render_management_test.js +++ b/tests/mocha/render_management_test.js @@ -57,4 +57,69 @@ suite('Render Management', function () { return promise; }); }); + + suite('triggering queued renders', function () { + function createMockBlock(ws) { + return { + hasRendered: false, + renderEfficiently: function () { + this.hasRendered = true; + }, + + // All of the APIs the render management system needs. + getParent: () => null, + getChildren: () => [], + isDisposed: () => false, + getRelativeToSurfaceXY: () => ({x: 0, y: 0}), + updateComponentLocations: () => {}, + workspace: ws || createMockWorkspace(), + }; + } + + function createMockWorkspace() { + return { + resizeContents: () => {}, + }; + } + + test('triggering queued renders rerenders blocks', function () { + const block = createMockBlock(); + Blockly.renderManagement.queueRender(block); + + Blockly.renderManagement.triggerQueuedRenders(); + + chai.assert.isTrue(block.hasRendered, 'Expected block to be rendered'); + }); + + test('triggering queued renders rerenders blocks in all workspaces', function () { + const workspace1 = createMockWorkspace(); + const workspace2 = createMockWorkspace(); + const block1 = createMockBlock(workspace1); + const block2 = createMockBlock(workspace2); + Blockly.renderManagement.queueRender(block1); + Blockly.renderManagement.queueRender(block2); + + Blockly.renderManagement.triggerQueuedRenders(); + + chai.assert.isTrue(block1.hasRendered, 'Expected block1 to be rendered'); + chai.assert.isTrue(block2.hasRendered, 'Expected block2 to be rendered'); + }); + + test('triggering queued renders in one workspace does not rerender blocks in another workspace', function () { + const workspace1 = createMockWorkspace(); + const workspace2 = createMockWorkspace(); + const block1 = createMockBlock(workspace1); + const block2 = createMockBlock(workspace2); + Blockly.renderManagement.queueRender(block1); + Blockly.renderManagement.queueRender(block2); + + Blockly.renderManagement.triggerQueuedRenders(workspace1); + + chai.assert.isTrue(block1.hasRendered, 'Expected block1 to be rendered'); + chai.assert.isFalse( + block2.hasRendered, + 'Expected block2 to not be rendered', + ); + }); + }); }); From dccedbfe96159441566083a7d9f2bd32758c587f Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Tue, 16 Jan 2024 14:23:58 -0800 Subject: [PATCH 12/31] fix: cancelling all renders on triggering queued renders (#7787) (cherry picked from commit 0d1245c6469667e9acc19887c3f47eea85c3bcbc) --- core/render_management.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/core/render_management.ts b/core/render_management.ts index d3f1ed4f1..f0ec2a133 100644 --- a/core/render_management.ts +++ b/core/render_management.ts @@ -12,7 +12,7 @@ import type {WorkspaceSvg} from './workspace_svg.js'; const rootBlocks = new Set(); /** The set of all blocks in need of rendering. */ -let dirtyBlocks = new WeakSet(); +const dirtyBlocks = new WeakSet(); /** * The promise which resolves after the current set of renders is completed. Or @@ -81,9 +81,9 @@ export function finishQueuedRenders(): Promise { * @internal */ export function triggerQueuedRenders(workspace?: WorkspaceSvg) { - window.cancelAnimationFrame(animationRequestId); + if (!workspace) window.cancelAnimationFrame(animationRequestId); doRenders(workspace); - if (afterRendersResolver) afterRendersResolver(); + if (!workspace && afterRendersResolver) afterRendersResolver(); } /** @@ -134,9 +134,19 @@ function doRenders(workspace?: WorkspaceSvg) { block.updateComponentLocations(blockOrigin); } - rootBlocks.clear(); - dirtyBlocks = new WeakSet(); - afterRendersPromise = null; + for (const block of blocks) { + dequeueBlock(block); + } + if (!workspace) afterRendersPromise = null; +} + +/** Removes the given block and children from the render queue. */ +function dequeueBlock(block: BlockSvg) { + rootBlocks.delete(block); + dirtyBlocks.delete(block); + for (const child of block.getChildren(false)) { + dequeueBlock(child); + } } /** From 960b89e8feb4f7d5394e3fa16bc83d08f75a335d Mon Sep 17 00:00:00 2001 From: Maribeth Bottorff Date: Wed, 17 Jan 2024 12:05:49 -0800 Subject: [PATCH 13/31] fix: first block dragged from flyout will have same id (#7788) (cherry picked from commit 49f65a235c361cbb931042ed9bb6da4c7c94b82a) --- core/flyout_base.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/flyout_base.ts b/core/flyout_base.ts index 1ee1a55d5..77eae8cca 100644 --- a/core/flyout_base.ts +++ b/core/flyout_base.ts @@ -1235,8 +1235,7 @@ export abstract class Flyout } // Clone the block. - // TODO(#7432): Add a saveIds parameter to `save`. - const json = blocks.save(oldBlock, {saveIds: false}) as blocks.State; + const json = blocks.save(oldBlock) as blocks.State; // Normallly this resizes leading to weird jumps. Save it for terminateDrag. targetWorkspace.setResizesEnabled(false); const block = blocks.append(json, targetWorkspace) as BlockSvg; From 498fc2c1ce3fec0a93c23b4d68c6adda207f4005 Mon Sep 17 00:00:00 2001 From: Maribeth Moffatt Date: Wed, 17 Jan 2024 12:31:55 -0800 Subject: [PATCH 14/31] release: Update version number to 10.3.1 --- 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 63fa0b775..d9fdbeded 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "blockly", - "version": "10.3.0", + "version": "10.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "blockly", - "version": "10.3.0", + "version": "10.3.1", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index aaf3fad2a..8c0b05750 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blockly", - "version": "10.3.0", + "version": "10.3.1", "description": "Blockly is a library for building visual programming editors.", "keywords": [ "blockly" From f2591dee89258232c29ef13800d7ca0495fd851c Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Thu, 18 Jan 2024 19:30:00 +0100 Subject: [PATCH 15/31] chore: Bump Python from 3.7 to 3.12 (#7791) 3.7 is not supported from 31 January. Failure to upgrade appears to result in the inability to push new versions. 3.12 is the latest available Python version for App Engine, and should last the longest. Tested on DOM-Tutorials and Glockenspiel. No issues there. --- appengine/app.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appengine/app.yaml b/appengine/app.yaml index 1778f2b93..9c93fb687 100644 --- a/appengine/app.yaml +++ b/appengine/app.yaml @@ -1,4 +1,4 @@ -runtime: python37 +runtime: python312 handlers: # Redirect obsolete URLs. From e715269f0b2292fd7f40edf9d7cd5c95dc727820 Mon Sep 17 00:00:00 2001 From: Ebrahim Haji <14169174+ebrahim95@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:13:32 -0600 Subject: [PATCH 16/31] fix: prevent block change event firing if editing is cancelled by using keypress (#7794) * fix: prevent block change event firing if editing is cancelled * refactor: formatted the code to specifications for commit 251c796d ran npm run format --- core/field_input.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/field_input.ts b/core/field_input.ts index 2cd4015b0..513047054 100644 --- a/core/field_input.ts +++ b/core/field_input.ts @@ -542,6 +542,7 @@ export abstract class FieldInput extends Field< } else if (e.key === 'Escape') { this.setValue( this.htmlInput_!.getAttribute('data-untyped-default-value'), + false, ); WidgetDiv.hide(); dropDownDiv.hideWithoutAnimation(); From 0efeff574ba8ba21f84c8465ac6f7e81ed0950e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:58:56 +0000 Subject: [PATCH 17/31] chore(deps): Bump eslint-config-prettier from 9.0.0 to 9.1.0 (#7752) Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 9.0.0 to 9.1.0. - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v9.0.0...v9.1.0) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a9325363..bded4b9d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3763,9 +3763,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" From 767b452aa9955bdf4edd53e84d3672b916692a57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:00:49 +0000 Subject: [PATCH 18/31] chore(deps): Bump @typescript-eslint/eslint-plugin from 6.13.2 to 6.19.0 (#7799) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.13.2 to 6.19.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.19.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 168 ++++++++++++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 59 deletions(-) diff --git a/package-lock.json b/package-lock.json index bded4b9d2..ee5dc0b60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -982,16 +982,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz", - "integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz", + "integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/type-utils": "6.13.2", - "@typescript-eslint/utils": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1017,13 +1017,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", - "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2" + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1034,9 +1034,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", - "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1047,12 +1047,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", - "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1111,13 +1111,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz", - "integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", + "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.2", - "@typescript-eslint/utils": "6.13.2", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1138,9 +1138,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", - "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1151,16 +1151,17 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", - "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -1178,12 +1179,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", - "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1194,6 +1195,30 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/types": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.1.0.tgz", @@ -1237,17 +1262,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz", - "integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.13.2", - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/typescript-estree": "6.13.2", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", "semver": "^7.5.4" }, "engines": { @@ -1262,13 +1287,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz", - "integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2" + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1279,9 +1304,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz", - "integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1292,16 +1317,17 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz", - "integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", - "@typescript-eslint/visitor-keys": "6.13.2", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -1319,12 +1345,12 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz", - "integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.2", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1335,6 +1361,30 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.1.0.tgz", From ff45a36835331c7e39e2ebed340ba8f0302eb75b Mon Sep 17 00:00:00 2001 From: Ebrahim Haji <14169174+ebrahim95@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:51:24 -0600 Subject: [PATCH 19/31] fix: shadow blocks with variable fields are not allowed to be deserialized from XML (#7802) --- core/xml.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/xml.ts b/core/xml.ts index c3b2d3ebc..ee72f526b 100644 --- a/core/xml.ts +++ b/core/xml.ts @@ -995,10 +995,7 @@ function domToBlockHeadless( throw TypeError('Shadow block not allowed non-shadow child.'); } } - // Ensure this block doesn't have any variable inputs. - if (block.getVarModels().length) { - throw TypeError('Shadow blocks cannot have variable references.'); - } + block.setShadow(true); } return block; From 0b25a8a27f156a8104cd242ee68de304506693cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:23:19 -0800 Subject: [PATCH 20/31] chore(deps): Bump webdriverio from 8.27.0 to 8.29.1 (#7812) Bumps [webdriverio](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/webdriverio) from 8.27.0 to 8.29.1. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/commits/v8.29.1/packages/webdriverio) --- updated-dependencies: - dependency-name: webdriverio dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 116 +++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index ee5dc0b60..f4dfd90d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1410,14 +1410,14 @@ "dev": true }, "node_modules/@wdio/config": { - "version": "8.27.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.27.0.tgz", - "integrity": "sha512-zYM5daeiBVVAbQj0ASymAt0RUsocLVIwKiUHNa8gg/1GsZnztGjetXExSp1gXlxtMVM5xWUSKjh6ceFK79gWDQ==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.29.1.tgz", + "integrity": "sha512-zNUac4lM429HDKAitO+fdlwUH1ACQU8lww+DNVgUyuEb86xgVdTqHeiJr/3kOMJAq9IATeE7mDtYyyn6HPm1JA==", "dev": true, "dependencies": { - "@wdio/logger": "8.24.12", - "@wdio/types": "8.27.0", - "@wdio/utils": "8.27.0", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", "decamelize": "^6.0.0", "deepmerge-ts": "^5.0.0", "glob": "^10.2.2", @@ -1440,9 +1440,9 @@ } }, "node_modules/@wdio/logger": { - "version": "8.24.12", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.24.12.tgz", - "integrity": "sha512-QisOiVIWKTUCf1H7S+DOtC+gruhlpimQrUXfWMTeeh672PvAJYnTpOJDWA+BtXfsikkUYFAzAaq8SeMJk8rqKg==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.28.0.tgz", + "integrity": "sha512-/s6zNCqwy1hoc+K4SJypis0Ud0dlJ+urOelJFO1x0G0rwDRWyFiUP6ijTaCcFxAm29jYEcEPWijl2xkVIHwOyA==", "dev": true, "dependencies": { "chalk": "^5.1.2", @@ -1512,9 +1512,9 @@ } }, "node_modules/@wdio/types": { - "version": "8.27.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.27.0.tgz", - "integrity": "sha512-LbP9FKh8r0uW9/dKhTIUCC1Su8PsP9TmzGKXkWt6/IMacgJiB/zW3u1CgyaLw9lG0UiQORHGoeJX9zB2HZAh4w==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.29.1.tgz", + "integrity": "sha512-rZYzu+sK8zY1PjCEWxNu4ELJPYKDZRn7HFcYNgR122ylHygfldwkb5TioI6Pn311hQH/S+663KEeoq//Jb0f8A==", "dev": true, "dependencies": { "@types/node": "^20.1.0" @@ -1524,14 +1524,14 @@ } }, "node_modules/@wdio/utils": { - "version": "8.27.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.27.0.tgz", - "integrity": "sha512-4BY+JBQssVn003P5lA289uDMie3LtGinHze5btkcW9timB6VaU+EeZS4eKTPC0pziizLhteVvXYxv3YTpeeRfA==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.29.1.tgz", + "integrity": "sha512-Dm91DKL/ZKeZ2QogWT8Twv0p+slEgKyB/5x9/kcCG0Q2nNa+tZedTjOhryzrsPiWc+jTSBmjGE4katRXpJRFJg==", "dev": true, "dependencies": { "@puppeteer/browsers": "^1.6.0", - "@wdio/logger": "8.24.12", - "@wdio/types": "8.27.0", + "@wdio/logger": "8.28.0", + "@wdio/types": "8.29.1", "decamelize": "^6.0.0", "deepmerge-ts": "^5.1.0", "edgedriver": "^5.3.5", @@ -1548,9 +1548,9 @@ } }, "node_modules/@wdio/utils/node_modules/@puppeteer/browsers": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.0.tgz", - "integrity": "sha512-QwguOLy44YBGC8vuPP2nmpX4MUN2FzWbsnvZJtiCzecU3lHmVZkaC1tq6rToi9a200m8RzlVtDyxCS0UIDrxUg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.9.1.tgz", + "integrity": "sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==", "dev": true, "dependencies": { "debug": "4.3.4", @@ -3389,9 +3389,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1237913", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1237913.tgz", - "integrity": "sha512-Pxtmz2ZIqBkpU82HaIdsvCQBG94yTC4xajrEsWx9p38QKEfBCJktSazsHkrjf9j3dVVNPhg5LR21F6KWeXpjiQ==", + "version": "0.0.1249869", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", + "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==", "dev": true }, "node_modules/dir-glob": { @@ -4781,9 +4781,9 @@ "dev": true }, "node_modules/geckodriver": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.3.0.tgz", - "integrity": "sha512-QfpvxFsMORwKpvnLslkHCr3NTCczHAvkte6+pQGsiUZXKBe6mO4TTb727b+9KMVSK6XZqhR6ZwImKdP+F5vS6A==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.3.1.tgz", + "integrity": "sha512-ol7JLsj55o5k+z7YzeSy2mdJROXMAxIa+uzr3A1yEMr5HISqQOTslE3ZeARcxR4jpAY3fxmHM+sq32qbe/eXfA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -7283,12 +7283,12 @@ } }, "node_modules/locate-app": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.1.0.tgz", - "integrity": "sha512-rcVo/iLUxrd9d0lrmregK/Z5Y5NCpSwf9KlMbPpOHmKmdxdQY1Fj8NDQ5QymJTryCsBLqwmniFv2f3JKbk9Bvg==", + "version": "2.2.14", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.2.14.tgz", + "integrity": "sha512-fqGE0IHZ3v+9kCjYvhwrP52aTGP1itOfp4TZZuv4dNl2gKN/pHCIlMhDSqPDb3qJ5Rti39y5T+/XrfCsiDRjKw==", "dev": true, "dependencies": { - "n12": "0.4.0", + "n12": "1.8.17", "type-fest": "2.13.0", "userhome": "1.0.0" } @@ -7392,9 +7392,9 @@ } }, "node_modules/loglevel": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", - "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", "dev": true, "engines": { "node": ">= 0.6.0" @@ -7985,9 +7985,9 @@ } }, "node_modules/n12": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/n12/-/n12-0.4.0.tgz", - "integrity": "sha512-p/hj4zQ8d3pbbFLQuN1K9honUxiDDhueOWyFLw/XgBv+wZCE44bcLH4CIcsolOceJQduh4Jf7m/LfaTxyGmGtQ==", + "version": "1.8.17", + "resolved": "https://registry.npmjs.org/n12/-/n12-1.8.17.tgz", + "integrity": "sha512-/NdfkU7nyqq70E4RvDa3OrR/wkZrYjDGXjn4JgIZnn+ULcyW1f6BLjNqyFC+ND9FqzyWjcQhvagFJmVJ8k8Lew==", "dev": true }, "node_modules/nanoid": { @@ -9650,9 +9650,9 @@ "dev": true }, "node_modules/safaridriver": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.1.tgz", - "integrity": "sha512-dpCmh2EYKh9G61nR+ve0w2+WW2YJX59Rtke0pUoUXbvGKPDLK+NcL7I3VBS1UcGJbA6ptQTT82JcGwJHALD0kQ==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-0.1.2.tgz", + "integrity": "sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==", "dev": true }, "node_modules/safe-buffer": { @@ -11361,27 +11361,27 @@ } }, "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", + "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/webdriver": { - "version": "8.27.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.27.0.tgz", - "integrity": "sha512-n1IA+rR3u84XxU9swiKUM06BkEC0GDimfZkBML57cny+utQOUbdM/mBpqCUnkWX/RBz/p2EfHdKNyOs3/REaog==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.29.1.tgz", + "integrity": "sha512-D3gkbDUxFKBJhNHRfMriWclooLbNavVQC1MRvmENAgPNKaHnFn+M+WtP9K2sEr0XczLGNlbOzT7CKR9K5UXKXA==", "dev": true, "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.27.0", - "@wdio/logger": "8.24.12", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", "@wdio/protocols": "8.24.12", - "@wdio/types": "8.27.0", - "@wdio/utils": "8.27.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", "deepmerge-ts": "^5.1.0", "got": "^12.6.1", "ky": "^0.33.0", @@ -11392,23 +11392,23 @@ } }, "node_modules/webdriverio": { - "version": "8.27.0", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.27.0.tgz", - "integrity": "sha512-Qh5VCiBjEmxnmXcL1QEFoDzFqTtaWKrXriuU5G0yHKCModGAt2G7IHTkAok3CpmkVJfZpEvY630aP1MvgDtFhw==", + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.29.1.tgz", + "integrity": "sha512-NZK95ivXCqdPraB3FHMw6ByxnCvtgFXkjzG2l3Oq5z0IuJS2aMow3AKFIyiuG6is/deGCe+Tb8eFTCqak7UV+w==", "dev": true, "dependencies": { "@types/node": "^20.1.0", - "@wdio/config": "8.27.0", - "@wdio/logger": "8.24.12", + "@wdio/config": "8.29.1", + "@wdio/logger": "8.28.0", "@wdio/protocols": "8.24.12", "@wdio/repl": "8.24.12", - "@wdio/types": "8.27.0", - "@wdio/utils": "8.27.0", + "@wdio/types": "8.29.1", + "@wdio/utils": "8.29.1", "archiver": "^6.0.0", "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools-protocol": "^0.0.1237913", + "devtools-protocol": "^0.0.1249869", "grapheme-splitter": "^1.0.2", "import-meta-resolve": "^4.0.0", "is-plain-obj": "^4.1.0", @@ -11420,7 +11420,7 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^11.0.1", - "webdriver": "8.27.0" + "webdriver": "8.29.1" }, "engines": { "node": "^16.13 || >=18" From d1cca3c09a4fa6d5410f446b9287ad4255b690a0 Mon Sep 17 00:00:00 2001 From: Maribeth Bottorff Date: Fri, 2 Feb 2024 10:52:36 -0800 Subject: [PATCH 21/31] fix: manually add typings for generator classes (#7824) * fix: manually add typings for generator classes * fix: use real generated type for class --- scripts/gulpfiles/package_tasks.js | 1 - tests/typescript/src/generators.ts | 31 ++++++++++++++++++++++++++++++ typings/dart.d.ts | 2 ++ typings/javascript.d.ts | 2 ++ typings/lua.d.ts | 2 ++ typings/php.d.ts | 2 ++ typings/python.d.ts | 2 ++ 7 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/typescript/src/generators.ts diff --git a/scripts/gulpfiles/package_tasks.js b/scripts/gulpfiles/package_tasks.js index 38a5b5f9a..46d91ec5f 100644 --- a/scripts/gulpfiles/package_tasks.js +++ b/scripts/gulpfiles/package_tasks.js @@ -348,7 +348,6 @@ function packageDTS() { return gulp.src(handwrittenSrcs, {base: 'typings'}) .pipe(gulp.src(`${TYPINGS_BUILD_DIR}/**/*.d.ts`, {ignore: [ `${TYPINGS_BUILD_DIR}/blocks/**/*`, - `${TYPINGS_BUILD_DIR}/generators/**/*`, ]})) .pipe(gulp.replace('AnyDuringMigration', 'any')) .pipe(gulp.dest(RELEASE_DIR)); diff --git a/tests/typescript/src/generators.ts b/tests/typescript/src/generators.ts new file mode 100644 index 000000000..a87d70ee3 --- /dev/null +++ b/tests/typescript/src/generators.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from 'blockly-test/core'; +import {JavascriptGenerator} from 'blockly-test/javascript'; +import {PhpGenerator, phpGenerator, Order} from 'blockly-test/php'; +import {LuaGenerator} from 'blockly-test/lua'; +import {PythonGenerator} from 'blockly-test/python'; +import {DartGenerator} from 'blockly-test/dart'; + +JavascriptGenerator; +PhpGenerator; +LuaGenerator; +PythonGenerator; +DartGenerator; + +class TestGenerator extends PhpGenerator {} + +const testGenerator = new TestGenerator(); + +testGenerator.forBlock['test_block'] = function ( + _block: Blockly.Block, + _generator: TestGenerator, +) { + return ['a fake code string', Order.ADDITION]; +}; + +phpGenerator.quote_(); diff --git a/typings/dart.d.ts b/typings/dart.d.ts index 4dda479a4..bf89e4f06 100644 --- a/typings/dart.d.ts +++ b/typings/dart.d.ts @@ -26,3 +26,5 @@ export enum Order { } export declare const dartGenerator: any; + +export {DartGenerator} from './generators/dart'; diff --git a/typings/javascript.d.ts b/typings/javascript.d.ts index b507a9be4..ed1106bbc 100644 --- a/typings/javascript.d.ts +++ b/typings/javascript.d.ts @@ -43,3 +43,5 @@ export enum Order { } export declare const javascriptGenerator: any; + +export {JavascriptGenerator} from './generators/javascript'; diff --git a/typings/lua.d.ts b/typings/lua.d.ts index 693fb0ba2..752e8521a 100644 --- a/typings/lua.d.ts +++ b/typings/lua.d.ts @@ -20,3 +20,5 @@ export enum Order { } export declare const luaGenerator: any; + +export {LuaGenerator} from './generators/lua'; diff --git a/typings/php.d.ts b/typings/php.d.ts index 29a223613..deaf9b899 100644 --- a/typings/php.d.ts +++ b/typings/php.d.ts @@ -45,3 +45,5 @@ export enum Order { } export declare const phpGenerator: any; + +export {PhpGenerator} from './generators/php'; diff --git a/typings/python.d.ts b/typings/python.d.ts index 6d6449e37..c0a2c284b 100644 --- a/typings/python.d.ts +++ b/typings/python.d.ts @@ -29,3 +29,5 @@ export enum Order { } export declare const pythonGenerator: any; + +export {PythonGenerator} from './generators/python'; From dc9aa1befb90b1a96b405f5fd33bcc0b420a7ac8 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Mon, 13 Nov 2023 17:20:28 +0000 Subject: [PATCH 22/31] feat: add connection previewer interface (#7637) * feat: add connection previewer interface * chore: PR comments --- core/blockly.ts | 2 + core/interfaces/i_connection_previewer.ts | 50 +++++++++++++++++++++++ core/registry.ts | 9 +++- 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 core/interfaces/i_connection_previewer.ts diff --git a/core/blockly.ts b/core/blockly.ts index 00c10f69e..9abea068e 100644 --- a/core/blockly.ts +++ b/core/blockly.ts @@ -138,6 +138,7 @@ import {IBubble} from './interfaces/i_bubble.js'; import {ICollapsibleToolboxItem} from './interfaces/i_collapsible_toolbox_item.js'; import {IComponent} from './interfaces/i_component.js'; import {IConnectionChecker} from './interfaces/i_connection_checker.js'; +import {IConnectionPreviewer} from './interfaces/i_connection_previewer.js'; import {IContextMenu} from './interfaces/i_contextmenu.js'; import {ICopyable, isCopyable} from './interfaces/i_copyable.js'; import {IDeletable} from './interfaces/i_deletable.js'; @@ -589,6 +590,7 @@ export {IBubble}; export {ICollapsibleToolboxItem}; export {IComponent}; export {IConnectionChecker}; +export {IConnectionPreviewer}; export {IContextMenu}; export {icons}; export {ICopyable, isCopyable}; diff --git a/core/interfaces/i_connection_previewer.ts b/core/interfaces/i_connection_previewer.ts new file mode 100644 index 000000000..df7906a29 --- /dev/null +++ b/core/interfaces/i_connection_previewer.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type {BlockSvg} from '../block_svg'; +import type {RenderedConnection} from '../rendered_connection'; + +/** + * Displays visual "previews" of where a block will be connected if it is + * dropped. + */ +export interface IConnectionPreviewer { + /** + * Display a connection preview where the draggedCon connects to the + * staticCon, replacing the replacedBlock (currently connected to the + * staticCon). + * + * @param draggedCon The connection on the block stack being dragged. + * @param staticCon The connection not being dragged that we are + * connecting to. + * @param replacedBlock The block currently connected to the staticCon that + * is being replaced. + */ + previewReplacement( + draggedConn: RenderedConnection, + staticConn: RenderedConnection, + replacedBlock: BlockSvg, + ): void; + + /** + * Display a connection preview where the draggedCon connects to the + * staticCon, and no block is being relaced. + * + * @param draggedCon The connection on the block stack being dragged. + * @param staticCon The connection not being dragged that we are + * connecting to. + */ + previewConnection( + draggedConn: RenderedConnection, + staticConn: RenderedConnection, + ): void; + + /** Hide any previews that are currently displayed. */ + hidePreview(): void; + + /** Dispose of any references held by this connection previewer. */ + dispose(): void; +} diff --git a/core/registry.ts b/core/registry.ts index 33bded304..3645a6fdf 100644 --- a/core/registry.ts +++ b/core/registry.ts @@ -21,8 +21,9 @@ import type {Options} from './options.js'; import type {Renderer} from './renderers/common/renderer.js'; import type {Theme} from './theme.js'; import type {ToolboxItem} from './toolbox/toolbox_item.js'; -import {IPaster} from './interfaces/i_paster.js'; -import {ICopyData, ICopyable} from './interfaces/i_copyable.js'; +import type {IPaster} from './interfaces/i_paster.js'; +import type {ICopyData, ICopyable} from './interfaces/i_copyable.js'; +import type {IConnectionPreviewer} from './interfaces/i_connection_previewer.js'; /** * A map of maps. With the keys being the type and name of the class we are @@ -66,6 +67,10 @@ export class Type<_T> { static CONNECTION_CHECKER = new Type('connectionChecker'); + static CONNECTION_PREVIEWER = new Type( + 'connectionPreviewer', + ); + static CURSOR = new Type('cursor'); static EVENT = new Type('event'); From 2c95c4202c12b3750675fe17b3965d8471d8d835 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 17 Nov 2023 15:55:42 +0000 Subject: [PATCH 23/31] feat: connection highlighter interface (#7638) * feat: add connection highlighter interface * fix: remove unnecessary method from the path object interface --- core/block_svg.ts | 9 +++++++-- core/interfaces/i_connection_highlighter.ts | 21 +++++++++++++++++++++ core/renderers/common/i_path_object.ts | 19 ------------------- 3 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 core/interfaces/i_connection_highlighter.ts diff --git a/core/block_svg.ts b/core/block_svg.ts index 2655642d7..f8f0c02ff 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -1722,7 +1722,8 @@ export class BlockSvg * @internal */ fadeForReplacement(add: boolean) { - this.pathObject.updateReplacementFade(add); + // TODO (7204): Remove these internal methods. + (this.pathObject as AnyDuringMigration).updateReplacementFade(add); } /** @@ -1734,6 +1735,10 @@ export class BlockSvg * @internal */ highlightShapeForInput(conn: RenderedConnection, add: boolean) { - this.pathObject.updateShapeForInputHighlight(conn, add); + // TODO (7204): Remove these internal methods. + (this.pathObject as AnyDuringMigration).updateShapeForInputHighlight( + conn, + add, + ); } } diff --git a/core/interfaces/i_connection_highlighter.ts b/core/interfaces/i_connection_highlighter.ts new file mode 100644 index 000000000..35fcf6776 --- /dev/null +++ b/core/interfaces/i_connection_highlighter.ts @@ -0,0 +1,21 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type {RenderedConnection} from '../rendered_connection'; + +/** + * Visually highlights connections, usually to preview where a block will be + * connected if it is dropped. + * + * Often implemented by IPathObject classes. + */ +export interface IConnectionHighlighter { + /** Visually highlights the given connection. */ + highlightConnection(conn: RenderedConnection): void; + + /** Visually unhighlights the given connnection (if it was highlighted). */ + unhighlightConnection(conn: RenderedConnection): void; +} diff --git a/core/renderers/common/i_path_object.ts b/core/renderers/common/i_path_object.ts index 5f7189bab..8bf57ddaa 100644 --- a/core/renderers/common/i_path_object.ts +++ b/core/renderers/common/i_path_object.ts @@ -9,7 +9,6 @@ import type {BlockStyle} from '../../theme.js'; import type {BlockSvg} from '../../block_svg.js'; import type {ConstantProvider} from './constants.js'; -import {RenderedConnection} from '../../rendered_connection.js'; /** * An interface for a block's path object. @@ -120,22 +119,4 @@ export interface IPathObject { * @param enable True if the block is movable, false otherwise. */ updateMovable(enabled: boolean): void; - - /** - * Add or remove styling that shows that if the dragging block is dropped, - * this block will be replaced. If a shadow block, it will disappear. - * Otherwise it will bump. - * - * @param enable True if styling should be added. - */ - updateReplacementFade(enabled: boolean): void; - - /** - * Add or remove styling that shows that if the dragging block is dropped, - * this block will be connected to the input. - * - * @param conn The connection on the input to highlight. - * @param enable True if styling should be added. - */ - updateShapeForInputHighlight(conn: RenderedConnection, enable: boolean): void; } From 461dfac05ac265b8e43b0d01e1987dbcd94f11ea Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Tue, 12 Dec 2023 16:33:36 +0000 Subject: [PATCH 24/31] feat: connection highlighting in geras and thrasos (#7698) * chore: move connection highlighting into the geras renderer * chore: remove IConnectionHighlighter interface * chore: format * chore: fixup * chore: format * fix: PR comments --- core/interfaces/i_connection_highlighter.ts | 21 ------ core/rendered_connection.ts | 61 +----------------- core/renderers/common/constants.ts | 20 +++++- core/renderers/common/drawer.ts | 71 ++++++++++++++++++++- core/renderers/common/i_path_object.ts | 15 +++++ core/renderers/common/info.ts | 21 +++++- core/renderers/common/path_object.ts | 40 ++++++++++++ core/renderers/common/renderer.ts | 19 ++++++ 8 files changed, 183 insertions(+), 85 deletions(-) delete mode 100644 core/interfaces/i_connection_highlighter.ts diff --git a/core/interfaces/i_connection_highlighter.ts b/core/interfaces/i_connection_highlighter.ts deleted file mode 100644 index 35fcf6776..000000000 --- a/core/interfaces/i_connection_highlighter.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import type {RenderedConnection} from '../rendered_connection'; - -/** - * Visually highlights connections, usually to preview where a block will be - * connected if it is dropped. - * - * Often implemented by IPathObject classes. - */ -export interface IConnectionHighlighter { - /** Visually highlights the given connection. */ - highlightConnection(conn: RenderedConnection): void; - - /** Visually unhighlights the given connnection (if it was highlighted). */ - unhighlightConnection(conn: RenderedConnection): void; -} diff --git a/core/rendered_connection.ts b/core/rendered_connection.ts index 08c0471a2..80fa035e6 100644 --- a/core/rendered_connection.ts +++ b/core/rendered_connection.ts @@ -23,18 +23,6 @@ import {hasBubble} from './interfaces/i_has_bubble.js'; import * as internalConstants from './internal_constants.js'; import {Coordinate} from './utils/coordinate.js'; import * as dom from './utils/dom.js'; -import {Svg} from './utils/svg.js'; -import * as svgPaths from './utils/svg_paths.js'; - -/** A shape that has a pathDown property. */ -interface PathDownShape { - pathDown: string; -} - -/** A shape that has a pathLeft property. */ -interface PathLeftShape { - pathLeft: string; -} /** Maximum randomness in workspace units for bumping a block. */ const BUMP_RANDOMNESS = 10; @@ -305,57 +293,12 @@ export class RenderedConnection extends Connection { /** Add highlighting around this connection. */ highlight() { - if (this.highlightPath) { - // This connection is already highlighted - return; - } - let steps; - const sourceBlockSvg = this.sourceBlock_; - const renderConstants = sourceBlockSvg.workspace - .getRenderer() - .getConstants(); - const shape = renderConstants.shapeFor(this); - if ( - this.type === ConnectionType.INPUT_VALUE || - this.type === ConnectionType.OUTPUT_VALUE - ) { - // Vertical line, puzzle tab, vertical line. - const yLen = renderConstants.TAB_OFFSET_FROM_TOP; - steps = - svgPaths.moveBy(0, -yLen) + - svgPaths.lineOnAxis('v', yLen) + - (shape as unknown as PathDownShape).pathDown + - svgPaths.lineOnAxis('v', yLen); - } else { - const xLen = - renderConstants.NOTCH_OFFSET_LEFT - renderConstants.CORNER_RADIUS; - // Horizontal line, notch, horizontal line. - steps = - svgPaths.moveBy(-xLen, 0) + - svgPaths.lineOnAxis('h', xLen) + - (shape as unknown as PathLeftShape).pathLeft + - svgPaths.lineOnAxis('h', xLen); - } - const offset = this.offsetInBlock; - this.highlightPath = dom.createSvgElement( - Svg.PATH, - { - 'class': 'blocklyHighlightedConnectionPath', - 'd': steps, - 'transform': - `translate(${offset.x}, ${offset.y})` + - (this.sourceBlock_.RTL ? ' scale(-1 1)' : ''), - }, - this.sourceBlock_.getSvgRoot(), - ); + this.getSourceBlock().workspace.getRenderer().highlightConnection(this); } /** Remove the highlighting around this connection. */ unhighlight() { - if (this.highlightPath) { - dom.removeNode(this.highlightPath); - this.highlightPath = null; - } + this.getSourceBlock().workspace.getRenderer().unhighlightConnection(this); } /** diff --git a/core/renderers/common/constants.ts b/core/renderers/common/constants.ts index f0acc39aa..1983793f0 100644 --- a/core/renderers/common/constants.ts +++ b/core/renderers/common/constants.ts @@ -53,8 +53,8 @@ export interface PuzzleTab { type: number; width: number; height: number; - pathDown: string | ((p1: number) => string); - pathUp: string | ((p1: number) => string); + pathDown: string; + pathUp: string; } /** @@ -100,6 +100,22 @@ export function isDynamicShape(shape: Shape): shape is DynamicShape { return (shape as DynamicShape).isDynamic; } +/** Returns whether the shape is a puzzle tab or not. */ +export function isPuzzleTab(shape: Shape): shape is PuzzleTab { + return ( + (shape as PuzzleTab).pathDown !== undefined && + (shape as PuzzleTab).pathUp !== undefined + ); +} + +/** Returns whether the shape is a notch or not. */ +export function isNotch(shape: Shape): shape is Notch { + return ( + (shape as Notch).pathLeft !== undefined && + (shape as Notch).pathRight !== undefined + ); +} + /** * An object that provides constants for rendering blocks. */ diff --git a/core/renderers/common/drawer.ts b/core/renderers/common/drawer.ts index e361a606d..bda4303af 100644 --- a/core/renderers/common/drawer.ts +++ b/core/renderers/common/drawer.ts @@ -18,10 +18,12 @@ import type {PreviousConnection} from '../measurables/previous_connection.js'; import type {Row} from '../measurables/row.js'; import {Types} from '../measurables/types.js'; -import {isDynamicShape} from './constants.js'; +import {isDynamicShape, isNotch, isPuzzleTab} from './constants.js'; import type {ConstantProvider, Notch, PuzzleTab} from './constants.js'; import type {RenderInfo} from './info.js'; import * as deprecation from '../../utils/deprecation.js'; +import type {RenderedConnection} from '../../rendered_connection.js'; +import {ConnectionType} from '../../connection_type.js'; /** * An object that draws a block based on the given rendering information. @@ -440,4 +442,71 @@ export class Drawer { ); } } + + /** Returns a path to highlight the given connection. */ + drawConnectionHighlightPath(conn: RenderedConnection) { + const measurable = this.info_.getMeasureableForConnection(conn); + if (!measurable) { + throw new Error('Could not find measurable for connection'); + } + + let path = ''; + if ( + conn.type === ConnectionType.INPUT_VALUE || + conn.type === ConnectionType.OUTPUT_VALUE + ) { + path = this.getExpressionConnectionHighlightPath(measurable); + } else { + path = this.getStatementConnectionHighlightPath(measurable); + } + const block = conn.getSourceBlock(); + block.pathObject.addConnectionHighlight?.( + conn, + path, + conn.getOffsetInBlock(), + block.RTL, + ); + } + + /** + * Returns a path to highlight the given conneciton, assuming it is an + * input or output connection. + */ + private getExpressionConnectionHighlightPath(connection: Connection): string { + let connPath = ''; + if (isDynamicShape(connection.shape)) { + connPath = connection.shape.pathDown(connection.height); + } else if (isPuzzleTab(connection.shape)) { + connPath = connection.shape.pathDown; + } + + // We are assuming that there is room for the tab offset above and below + // the tab. + const yLen = this.constants_.TAB_OFFSET_FROM_TOP; + return ( + svgPaths.moveBy(0, -yLen) + + svgPaths.lineOnAxis('v', yLen) + + connPath + + svgPaths.lineOnAxis('v', yLen) + ); + } + + /** + * Returns a path to highlight the given conneciton, assuming it is a + * next or previous connection. + */ + private getStatementConnectionHighlightPath(connection: Connection): string { + if (!isNotch(connection.shape)) { + throw new Error('Statement connections should have notch shapes'); + } + + const xLen = + this.constants_.NOTCH_OFFSET_LEFT - this.constants_.CORNER_RADIUS; + return ( + svgPaths.moveBy(-xLen, 0) + + svgPaths.lineOnAxis('h', xLen) + + connection.shape.pathLeft + + svgPaths.lineOnAxis('h', xLen) + ); + } } diff --git a/core/renderers/common/i_path_object.ts b/core/renderers/common/i_path_object.ts index 8bf57ddaa..30033f18e 100644 --- a/core/renderers/common/i_path_object.ts +++ b/core/renderers/common/i_path_object.ts @@ -9,6 +9,8 @@ import type {BlockStyle} from '../../theme.js'; import type {BlockSvg} from '../../block_svg.js'; import type {ConstantProvider} from './constants.js'; +import type {RenderedConnection} from '../../rendered_connection.js'; +import type {Coordinate} from '../../utils/coordinate.js'; /** * An interface for a block's path object. @@ -119,4 +121,17 @@ export interface IPathObject { * @param enable True if the block is movable, false otherwise. */ updateMovable(enabled: boolean): void; + + /** Adds the given path as a connection highlight for the given connection. */ + addConnectionHighlight?( + connection: RenderedConnection, + connectionPath: string, + offset: Coordinate, + rtl: boolean, + ): void; + + /** + * Removes any highlight associated with the given connection, if it exists. + */ + removeConnectionHighlight?(connection: RenderedConnection): void; } diff --git a/core/renderers/common/info.ts b/core/renderers/common/info.ts index fa44b7743..995124c1b 100644 --- a/core/renderers/common/info.ts +++ b/core/renderers/common/info.ts @@ -37,6 +37,7 @@ import {ValueInput} from '../../inputs/value_input.js'; import type {ConstantProvider} from './constants.js'; import type {Renderer} from './renderer.js'; +import {Connection} from '../measurables/connection.js'; /** * An object containing all sizing information needed to draw this block. @@ -143,8 +144,7 @@ export class RenderInfo { } /** - * Populate and return an object containing all sizing information needed to - * draw this block. + * Populate this object with all sizing information needed to draw the block. * * This measure pass does not propagate changes to the block (although fields * may choose to rerender when getSize() is called). However, calling it @@ -748,4 +748,21 @@ export class RenderInfo { this.startY = this.topRow.capline; this.bottomRow.baseline = yCursor - this.bottomRow.descenderHeight; } + + /** Returns the connection measurable associated with the given connection. */ + getMeasureableForConnection(conn: RenderedConnection): Connection | null { + if (this.outputConnection?.connectionModel === conn) { + return this.outputConnection; + } + + for (const row of this.rows) { + for (const elem of row.elements) { + if (elem instanceof Connection && elem.connectionModel === conn) { + return elem; + } + } + } + + return null; + } } diff --git a/core/renderers/common/path_object.ts b/core/renderers/common/path_object.ts index f5987f86e..35e1f83c5 100644 --- a/core/renderers/common/path_object.ts +++ b/core/renderers/common/path_object.ts @@ -8,7 +8,9 @@ import type {BlockSvg} from '../../block_svg.js'; import type {Connection} from '../../connection.js'; +import {RenderedConnection} from '../../rendered_connection.js'; import type {BlockStyle} from '../../theme.js'; +import {Coordinate} from '../../utils/coordinate.js'; import * as dom from '../../utils/dom.js'; import {Svg} from '../../utils/svg.js'; @@ -38,6 +40,13 @@ export class PathObject implements IPathObject { constants: ConstantProvider; style: BlockStyle; + /** + * Highlight paths associated with connections. + * + * @protected + */ + connectionHighlights = new WeakMap(); + /** * @param root The root SVG element. * @param style The style object to use for colouring. @@ -256,4 +265,35 @@ export class PathObject implements IPathObject { updateShapeForInputHighlight(_conn: Connection, _enable: boolean) { // NOOP } + + /** Adds the given path as a connection highlight for the given connection. */ + addConnectionHighlight( + connection: RenderedConnection, + connectionPath: string, + offset: Coordinate, + rtl: boolean, + ) { + if (this.connectionHighlights.has(connection)) return; + const highlight = dom.createSvgElement( + Svg.PATH, + { + 'class': 'blocklyHighlightedConnectionPath', + 'd': connectionPath, + 'transform': + `translate(${offset.x}, ${offset.y})` + (rtl ? ' scale(-1 1)' : ''), + }, + this.svgRoot, + ); + this.connectionHighlights.set(connection, highlight); + } + + /** + * Removes any highlight associated with the given connection, if it exists. + */ + removeConnectionHighlight(connection: RenderedConnection) { + const highlight = this.connectionHighlights.get(connection); + if (!highlight) return; + dom.removeNode(highlight); + this.connectionHighlights.delete(connection); + } } diff --git a/core/renderers/common/renderer.ts b/core/renderers/common/renderer.ts index 3a53322dd..d171f8b09 100644 --- a/core/renderers/common/renderer.ts +++ b/core/renderers/common/renderer.ts @@ -257,6 +257,25 @@ export class Renderer implements IRegistrable { return InsertionMarkerManager.PREVIEW_TYPE.INSERTION_MARKER; } + /** + * Visually highlights the given connection with a border, if it is not + * already highlighted. + */ + highlightConnection(conn: RenderedConnection): void { + const block = conn.getSourceBlock(); + + const info = this.makeRenderInfo_(block); + info.measure(); + const drawer = this.makeDrawer_(block, info); + + drawer.drawConnectionHighlightPath(conn); + } + + /** Visually unhighlights the given connection, if it is highlighted. */ + unhighlightConnection(conn: RenderedConnection): void { + conn.getSourceBlock().pathObject.removeConnectionHighlight?.(conn); + } + /** * Render the block. * From 318621d3c0535ba3c745a3b2d1703b110f1230f3 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 12 Jan 2024 08:35:36 -0800 Subject: [PATCH 25/31] feat: add connection highlighting to zelos (#7781) * feat: add connection highlighting to zelos * fix: drawing outputs * chore: cleanup --- core/renderers/zelos/constants.ts | 22 +++++----- core/renderers/zelos/drawer.ts | 72 +++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/core/renderers/zelos/constants.ts b/core/renderers/zelos/constants.ts index 73a600c58..1eabd7edd 100644 --- a/core/renderers/zelos/constants.ts +++ b/core/renderers/zelos/constants.ts @@ -392,19 +392,20 @@ export class ConstantProvider extends BaseConstantProvider { blockHeight > maxHeight ? blockHeight - maxHeight : 0; const height = blockHeight > maxHeight ? maxHeight : blockHeight; const radius = height / 2; + const sweep = right === up ? '0' : '1'; return ( svgPaths.arc( 'a', - '0 0,1', + '0 0,' + sweep, radius, - svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius), + svgPaths.point((right ? 1 : -1) * radius, (up ? -1 : 1) * radius), ) + - svgPaths.lineOnAxis('v', (right ? 1 : -1) * remainingHeight) + + svgPaths.lineOnAxis('v', (up ? -1 : 1) * remainingHeight) + svgPaths.arc( 'a', - '0 0,1', + '0 0,' + sweep, radius, - svgPaths.point((up ? 1 : -1) * radius, (up ? -1 : 1) * radius), + svgPaths.point((right ? -1 : 1) * radius, (up ? -1 : 1) * radius), ) ); } @@ -465,19 +466,20 @@ export class ConstantProvider extends BaseConstantProvider { */ function makeMainPath(height: number, up: boolean, right: boolean): string { const innerHeight = height - radius * 2; + const sweep = right === up ? '0' : '1'; return ( svgPaths.arc( 'a', - '0 0,1', + '0 0,' + sweep, radius, - svgPaths.point((up ? -1 : 1) * radius, (up ? -1 : 1) * radius), + svgPaths.point((right ? 1 : -1) * radius, (up ? -1 : 1) * radius), ) + - svgPaths.lineOnAxis('v', (right ? 1 : -1) * innerHeight) + + svgPaths.lineOnAxis('v', (up ? -1 : 1) * innerHeight) + svgPaths.arc( 'a', - '0 0,1', + '0 0,' + sweep, radius, - svgPaths.point((up ? 1 : -1) * radius, (up ? -1 : 1) * radius), + svgPaths.point((right ? -1 : 1) * radius, (up ? -1 : 1) * radius), ) ); } diff --git a/core/renderers/zelos/drawer.ts b/core/renderers/zelos/drawer.ts index ffdc3ca58..1f78047f0 100644 --- a/core/renderers/zelos/drawer.ts +++ b/core/renderers/zelos/drawer.ts @@ -7,10 +7,13 @@ // Former goog.module ID: Blockly.zelos.Drawer import type {BlockSvg} from '../../block_svg.js'; +import {ConnectionType} from '../../connection_type.js'; +import {RenderedConnection} from '../../rendered_connection.js'; import * as svgPaths from '../../utils/svg_paths.js'; import type {BaseShape, DynamicShape, Notch} from '../common/constants.js'; import {Drawer as BaseDrawer} from '../common/drawer.js'; import type {InlineInput} from '../measurables/inline_input.js'; +import {OutputConnection} from '../measurables/output_connection.js'; import type {Row} from '../measurables/row.js'; import type {SpacerRow} from '../measurables/spacer_row.js'; import {Types} from '../measurables/types.js'; @@ -179,21 +182,27 @@ export class Drawer extends BaseDrawer { return; } - const width = input.width - input.connectionWidth * 2; - const height = input.height; - const yPos = input.centerline - height / 2; - + const yPos = input.centerline - input.height / 2; const connectionRight = input.xPos + input.connectionWidth; - const outlinePath = - svgPaths.moveTo(connectionRight, yPos) + - svgPaths.lineOnAxis('h', width) + - (input.shape as DynamicShape).pathRightDown(input.height) + - svgPaths.lineOnAxis('h', -width) + - (input.shape as DynamicShape).pathUp(input.height) + - 'z'; + const path = + svgPaths.moveTo(connectionRight, yPos) + this.getInlineInputPath(input); + const pathObject = this.block_.pathObject as PathObject; - pathObject.setOutlinePath(inputName, outlinePath); + pathObject.setOutlinePath(inputName, path); + } + + private getInlineInputPath(input: InlineInput) { + const width = input.width - input.connectionWidth * 2; + const height = input.height; + + return ( + svgPaths.lineOnAxis('h', width) + + (input.shape as DynamicShape).pathRightDown(height) + + svgPaths.lineOnAxis('h', -width) + + (input.shape as DynamicShape).pathUp(height) + + 'z' + ); } override drawStatementInput_(row: Row) { @@ -225,4 +234,43 @@ export class Drawer extends BaseDrawer { this.positionStatementInputConnection_(row); } + + /** Returns a path to highlight the given connection. */ + drawConnectionHighlightPath(conn: RenderedConnection) { + const measurable = this.info_.getMeasureableForConnection(conn); + if (!measurable) { + throw new Error('Could not find measurable for connection'); + } + if ( + conn.type === ConnectionType.NEXT_STATEMENT || + conn.type === ConnectionType.PREVIOUS_STATEMENT || + (conn.type === ConnectionType.OUTPUT_VALUE && !measurable.isDynamicShape) + ) { + super.drawConnectionHighlightPath(conn); + return; + } + + let path = ''; + if (conn.type === ConnectionType.INPUT_VALUE) { + const input = measurable as InlineInput; + const xPos = input.connectionWidth; + const yPos = -input.height / 2; + path = svgPaths.moveTo(xPos, yPos) + this.getInlineInputPath(input); + } else { + // Dynamic output. + const output = measurable as OutputConnection; + const xPos = output.width; + const yPos = -output.height / 2; + path = + svgPaths.moveTo(xPos, yPos) + + (output.shape as DynamicShape).pathDown(output.height); + } + const block = conn.getSourceBlock(); + block.pathObject.addConnectionHighlight?.( + conn, + path, + conn.getOffsetInBlock(), + block.RTL, + ); + } } From f2c06fa907f5c99b5a099925575908407e97f120 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 19 Jan 2024 14:16:27 -0800 Subject: [PATCH 26/31] chore: delete highlight path from connection (#7796) --- core/rendered_connection.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/rendered_connection.ts b/core/rendered_connection.ts index 80fa035e6..d26e34d44 100644 --- a/core/rendered_connection.ts +++ b/core/rendered_connection.ts @@ -22,7 +22,6 @@ import * as eventUtils from './events/utils.js'; import {hasBubble} from './interfaces/i_has_bubble.js'; import * as internalConstants from './internal_constants.js'; import {Coordinate} from './utils/coordinate.js'; -import * as dom from './utils/dom.js'; /** Maximum randomness in workspace units for bumping a block. */ const BUMP_RANDOMNESS = 10; @@ -37,7 +36,6 @@ export class RenderedConnection extends Connection { private readonly dbOpposite: ConnectionDB; private readonly offsetInBlock: Coordinate; private trackedState: TrackedState; - private highlightPath: SVGPathElement | null = null; /** Connection this connection connects to. Null if not connected. */ override targetConnection: RenderedConnection | null = null; @@ -80,10 +78,6 @@ export class RenderedConnection extends Connection { if (this.trackedState === RenderedConnection.TrackedState.TRACKED) { this.db.removeConnection(this, this.y); } - if (this.highlightPath) { - dom.removeNode(this.highlightPath); - this.highlightPath = null; - } } /** From 038c4afec0bb73c60ea138adbf9a4767fccad66c Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Mon, 22 Jan 2024 12:01:37 -0800 Subject: [PATCH 27/31] feat: insertion marker previewer (#7792) * chore: add noop InsertionMarkerPreviewer * feat: add previewing replacements * feat: add previewing insertions * feat: add highlighting of connections to previewer * fix: disabling events in the connection previewer * chore: fixup docs * chore: typo --- .../insertion_marker_previewer.ts | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 core/connection_previewers/insertion_marker_previewer.ts diff --git a/core/connection_previewers/insertion_marker_previewer.ts b/core/connection_previewers/insertion_marker_previewer.ts new file mode 100644 index 000000000..73f620dca --- /dev/null +++ b/core/connection_previewers/insertion_marker_previewer.ts @@ -0,0 +1,249 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {BlockSvg} from '../block_svg.js'; +import {IConnectionPreviewer} from '../interfaces/i_connection_previewer.js'; +import {RenderedConnection} from '../rendered_connection.js'; +import {WorkspaceSvg} from '../workspace_svg.js'; +import * as eventUtils from '../events/utils.js'; +import * as constants from '../constants.js'; +import * as renderManagement from '../render_management.js'; + +/** + * An error message to throw if the block created by createMarkerBlock_ is + * missing any components. + */ +const DUPLICATE_BLOCK_ERROR = + 'The insertion marker previewer tried to create a marker but the result ' + + 'is missing %1. If you are using a mutator, make sure your domToMutation ' + + 'method is properly defined.'; + +export class InsertionMarkerPreviewer implements IConnectionPreviewer { + private readonly workspace: WorkspaceSvg; + + private fadedBlock: BlockSvg | null = null; + + private markerConn: RenderedConnection | null = null; + + private draggedConn: RenderedConnection | null = null; + + private staticConn: RenderedConnection | null = null; + + constructor(draggedBlock: BlockSvg) { + this.workspace = draggedBlock.workspace; + } + + /** + * Display a connection preview where the draggedCon connects to the + * staticCon, replacing the replacedBlock (currently connected to the + * staticCon). + * + * @param draggedConn The connection on the block stack being dragged. + * @param staticConn The connection not being dragged that we are + * connecting to. + * @param replacedBlock The block currently connected to the staticCon that + * is being replaced. + */ + previewReplacement( + draggedConn: RenderedConnection, + staticConn: RenderedConnection, + replacedBlock: BlockSvg, + ) { + eventUtils.disable(); + try { + this.hidePreview(); + this.fadedBlock = replacedBlock; + replacedBlock.fadeForReplacement(true); + if (this.workspace.getRenderer().shouldHighlightConnection(staticConn)) { + staticConn.highlight(); + this.staticConn = staticConn; + } + } finally { + eventUtils.enable(); + } + } + + /** + * Display a connection preview where the draggedCon connects to the + * staticCon, and no block is being relaced. + * + * @param draggedConn The connection on the block stack being dragged. + * @param staticConn The connection not being dragged that we are + * connecting to. + */ + previewConnection( + draggedConn: RenderedConnection, + staticConn: RenderedConnection, + ) { + if (draggedConn === this.draggedConn && staticConn === this.staticConn) { + return; + } + + eventUtils.disable(); + try { + this.hidePreview(); + const dragged = draggedConn.getSourceBlock(); + const marker = this.createInsertionMarker(dragged); + const markerConn = this.getMatchingConnection( + dragged, + marker, + draggedConn, + ); + if (!markerConn) { + throw Error('Could not create insertion marker to preview connection'); + } + + // Render disconnected from everything else so that we have a valid + // connection location. + marker.queueRender(); + renderManagement.triggerQueuedRenders(); + + // Connect() also renders the insertion marker. + markerConn.connect(staticConn); + + const originalOffsetToTarget = { + x: staticConn.x - markerConn.x, + y: staticConn.y - markerConn.y, + }; + const originalOffsetInBlock = markerConn.getOffsetInBlock().clone(); + renderManagement.finishQueuedRenders().then(() => { + // Position so that the existing block doesn't move. + marker?.positionNearConnection( + markerConn, + originalOffsetToTarget, + originalOffsetInBlock, + ); + marker?.getSvgRoot().setAttribute('visibility', 'visible'); + }); + + if (this.workspace.getRenderer().shouldHighlightConnection(staticConn)) { + staticConn.highlight(); + } + + this.markerConn = markerConn; + this.draggedConn = draggedConn; + this.staticConn = staticConn; + } finally { + eventUtils.enable(); + } + } + + private createInsertionMarker(origBlock: BlockSvg) { + const result = this.workspace.newBlock(origBlock.type); + result.setInsertionMarker(true); + if (origBlock.saveExtraState) { + const state = origBlock.saveExtraState(true); + if (state && result.loadExtraState) { + result.loadExtraState(state); + } + } else if (origBlock.mutationToDom) { + const oldMutationDom = origBlock.mutationToDom(); + if (oldMutationDom && result.domToMutation) { + result.domToMutation(oldMutationDom); + } + } + // Copy field values from the other block. These values may impact the + // rendered size of the insertion marker. Note that we do not care about + // child blocks here. + for (let i = 0; i < origBlock.inputList.length; i++) { + const sourceInput = origBlock.inputList[i]; + if (sourceInput.name === constants.COLLAPSED_INPUT_NAME) { + continue; // Ignore the collapsed input. + } + const resultInput = result.inputList[i]; + if (!resultInput) { + throw new Error(DUPLICATE_BLOCK_ERROR.replace('%1', 'an input')); + } + for (let j = 0; j < sourceInput.fieldRow.length; j++) { + const sourceField = sourceInput.fieldRow[j]; + const resultField = resultInput.fieldRow[j]; + if (!resultField) { + throw new Error(DUPLICATE_BLOCK_ERROR.replace('%1', 'a field')); + } + resultField.setValue(sourceField.getValue()); + } + } + + for (const block of result.getDescendants(false)) { + block.setInsertionMarker(true); + } + + result.setCollapsed(origBlock.isCollapsed()); + result.setInputsInline(origBlock.getInputsInline()); + + result.initSvg(); + result.getSvgRoot().setAttribute('visibility', 'hidden'); + return result; + } + + /** + * Gets the connection on the marker block that matches the original + * connection on the original block. + * + * @param orig The original block. + * @param marker The marker block (where we want to find the matching + * connection). + * @param origConn The original connection. + */ + private getMatchingConnection( + orig: BlockSvg, + marker: BlockSvg, + origConn: RenderedConnection, + ) { + const origConns = orig.getConnections_(true); + const markerConns = marker.getConnections_(true); + for (let i = 0; i < origConns.length; i++) { + if (origConns[i] === origConn) { + return markerConns[i]; + } + } + return null; + } + + /** Hide any previews that are currently displayed. */ + hidePreview() { + eventUtils.disable(); + try { + if (this.staticConn) { + this.staticConn.unhighlight(); + this.staticConn = null; + } + if (this.fadedBlock) { + this.fadedBlock.fadeForReplacement(false); + this.fadedBlock = null; + } + if (this.markerConn) { + this.hideInsertionMarker(this.markerConn); + this.markerConn = null; + this.draggedConn = null; + } + } finally { + eventUtils.enable(); + } + } + + private hideInsertionMarker(markerConn: RenderedConnection) { + const marker = markerConn.getSourceBlock(); + const markerPrev = marker.previousConnection; + const markerOutput = marker.outputConnection; + + if (!markerPrev?.targetConnection && !markerOutput?.targetConnection) { + // If we are the top block, unplugging doesn't do anything. + // The marker connection may not have a target block if we are hiding + // as part of applying connections. + markerConn.targetBlock()?.unplug(false); + } else { + marker.unplug(true); + } + + marker.dispose(); + } + + /** Dispose of any references held by this connection previewer. */ + dispose() { + this.hidePreview(); + } +} From 0b344e7fab3ba50eaf7e288ece5d6f5036388c19 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Thu, 25 Jan 2024 10:06:40 -0800 Subject: [PATCH 28/31] feat: update block dragger to use connection previewer (#7793) * chore: reorganize drag logic * chore: implement would delete block logic * fix: just use snap radius. Turns out the numbers are the same. * chore: add connection previewing * fix: applying connections * chore: remove dragged connection manager from block dragger * fix: deleting blocks --- core/block_dragger.ts | 254 +++++++++++++++++++++++++++++++++++------- core/config.ts | 1 + 2 files changed, 217 insertions(+), 38 deletions(-) diff --git a/core/block_dragger.ts b/core/block_dragger.ts index 78c1381ae..57c77bb0d 100644 --- a/core/block_dragger.ts +++ b/core/block_dragger.ts @@ -21,7 +21,6 @@ import * as common from './common.js'; import type {BlockMove} from './events/events_block_move.js'; import * as eventUtils from './events/utils.js'; import type {Icon} from './icons/icon.js'; -import {InsertionMarkerManager} from './insertion_marker_manager.js'; import type {IBlockDragger} from './interfaces/i_block_dragger.js'; import type {IDragTarget} from './interfaces/i_drag_target.js'; import * as registry from './registry.js'; @@ -31,6 +30,27 @@ import type {WorkspaceSvg} from './workspace_svg.js'; import {hasBubble} from './interfaces/i_has_bubble.js'; import * as deprecation from './utils/deprecation.js'; import * as layers from './layers.js'; +import {ConnectionType, IConnectionPreviewer} from './blockly.js'; +import {InsertionMarkerPreviewer} from './connection_previewers/insertion_marker_previewer.js'; +import {RenderedConnection} from './rendered_connection.js'; +import {config} from './config.js'; +import {ComponentManager} from './component_manager.js'; +import {IDeleteArea} from './interfaces/i_delete_area.js'; +import {Connection} from './connection.js'; +import {Block} from './block.js'; +import {finishQueuedRenders} from './render_management.js'; + +/** Represents a nearby valid connection. */ +interface ConnectionCandidate { + /** A connection on the dragging stack that is compatible with neighbour. */ + local: RenderedConnection; + + /** A nearby connection that is compatible with local. */ + neighbour: RenderedConnection; + + /** The distance between the local connection and the neighbour connection. */ + distance: number; +} /** * Class for a block dragger. It moves blocks around the workspace when they @@ -39,7 +59,8 @@ import * as layers from './layers.js'; export class BlockDragger implements IBlockDragger { /** The top block in the stack that is being dragged. */ protected draggingBlock_: BlockSvg; - protected draggedConnectionManager_: InsertionMarkerManager; + + protected connectionPreviewer: IConnectionPreviewer; /** The workspace on which the block is being dragged. */ protected workspace_: WorkspaceSvg; @@ -47,6 +68,8 @@ export class BlockDragger implements IBlockDragger { /** Which drag area the mouse pointer is over, if any. */ private dragTarget_: IDragTarget | null = null; + private connectionCandidate: ConnectionCandidate | null = null; + /** Whether the block would be deleted if dropped immediately. */ protected wouldDeleteBlock_ = false; protected startXY_: Coordinate; @@ -64,10 +87,8 @@ export class BlockDragger implements IBlockDragger { constructor(block: BlockSvg, workspace: WorkspaceSvg) { this.draggingBlock_ = block; - /** Object that keeps track of connections on dragged blocks. */ - this.draggedConnectionManager_ = new InsertionMarkerManager( - this.draggingBlock_, - ); + // TODO: have this access the registry instead. + this.connectionPreviewer = new InsertionMarkerPreviewer(block); this.workspace_ = workspace; @@ -87,9 +108,7 @@ export class BlockDragger implements IBlockDragger { */ dispose() { this.dragIconData_.length = 0; - if (this.draggedConnectionManager_) { - this.draggedConnectionManager_.dispose(); - } + this.connectionPreviewer.dispose(); } /** @@ -155,7 +174,6 @@ export class BlockDragger implements IBlockDragger { this.draggingBlock_.translate(newLoc.x, newLoc.y); blockAnimation.disconnectUiEffect(this.draggingBlock_); - this.draggedConnectionManager_.updateAvailableConnections(); } /** Fire a UI event at the start of a block drag. */ @@ -173,32 +191,178 @@ export class BlockDragger implements IBlockDragger { * display accordingly. * * @param e The most recent move event. - * @param currentDragDeltaXY How far the pointer has moved from the position + * @param delta How far the pointer has moved from the position * at the start of the drag, in pixel units. */ - drag(e: PointerEvent, currentDragDeltaXY: Coordinate) { - const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); + drag(e: PointerEvent, delta: Coordinate) { + const block = this.draggingBlock_; + this.moveBlock(block, delta); + this.updateDragTargets(e, block); + this.wouldDeleteBlock_ = this.wouldDeleteBlock(e, block, delta); + this.updateCursorDuringBlockDrag_(); + this.updateConnectionPreview(block, delta); + } + + private moveBlock(draggingBlock: BlockSvg, dragDelta: Coordinate) { + const delta = this.pixelsToWorkspaceUnits_(dragDelta); const newLoc = Coordinate.sum(this.startXY_, delta); - this.draggingBlock_.moveDuringDrag(newLoc); + draggingBlock.moveDuringDrag(newLoc); + } - const oldDragTarget = this.dragTarget_; - this.dragTarget_ = this.workspace_.getDragTarget(e); + private updateDragTargets(e: PointerEvent, draggingBlock: BlockSvg) { + const newDragTarget = this.workspace_.getDragTarget(e); + if (this.dragTarget_ !== newDragTarget) { + this.dragTarget_?.onDragExit(draggingBlock); + newDragTarget?.onDragEnter(draggingBlock); + } + newDragTarget?.onDragOver(draggingBlock); + this.dragTarget_ = newDragTarget; + } - this.draggedConnectionManager_.update(delta, this.dragTarget_); - const oldWouldDeleteBlock = this.wouldDeleteBlock_; - this.wouldDeleteBlock_ = this.draggedConnectionManager_.wouldDeleteBlock; - if (oldWouldDeleteBlock !== this.wouldDeleteBlock_) { - // Prevent unnecessary add/remove class calls. - this.updateCursorDuringBlockDrag_(); + /** + * Returns true if we would delete the block if it was dropped at this time, + * false otherwise. + */ + private wouldDeleteBlock( + e: PointerEvent, + draggingBlock: BlockSvg, + delta: Coordinate, + ): boolean { + const dragTarget = this.workspace_.getDragTarget(e); + if (!dragTarget) return false; + + const componentManager = this.workspace_.getComponentManager(); + const isDeleteArea = componentManager.hasCapability( + dragTarget.id, + ComponentManager.Capability.DELETE_AREA, + ); + if (!isDeleteArea) return false; + + return (dragTarget as IDeleteArea).wouldDelete( + draggingBlock, + !!this.getConnectionCandidate(draggingBlock, delta), + ); + } + + private updateConnectionPreview(draggingBlock: BlockSvg, delta: Coordinate) { + const currCandidate = this.connectionCandidate; + const newCandidate = this.getConnectionCandidate(draggingBlock, delta); + if (!newCandidate) { + this.connectionPreviewer.hidePreview(); + this.connectionCandidate = null; + return; + } + const candidate = + currCandidate && + this.currCandidateIsBetter(currCandidate, delta, newCandidate) + ? currCandidate + : newCandidate; + this.connectionCandidate = candidate; + const {local, neighbour} = candidate; + if ( + (local.type === ConnectionType.OUTPUT_VALUE || + local.type === ConnectionType.PREVIOUS_STATEMENT) && + neighbour.isConnected() && + !neighbour.targetBlock()!.isInsertionMarker() && + !this.orphanCanConnectAtEnd( + draggingBlock, + neighbour.targetBlock()!, + local.type, + ) + ) { + this.connectionPreviewer.previewReplacement( + local, + neighbour, + neighbour.targetBlock()!, + ); + return; + } + this.connectionPreviewer.previewConnection(local, neighbour); + } + + /** + * Returns true if the given orphan block can connect at the end of the + * top block's stack or row, false otherwise. + */ + private orphanCanConnectAtEnd( + topBlock: BlockSvg, + orphanBlock: BlockSvg, + localType: number, + ): boolean { + const orphanConnection = + localType === ConnectionType.OUTPUT_VALUE + ? orphanBlock.outputConnection + : orphanBlock.previousConnection; + return !!Connection.getConnectionForOrphanedConnection( + topBlock as Block, + orphanConnection as Connection, + ); + } + + /** + * Returns true if the current candidate is better than the new candidate. + * + * We slightly prefer the current candidate even if it is farther away. + */ + private currCandidateIsBetter( + currCandiate: ConnectionCandidate, + delta: Coordinate, + newCandidate: ConnectionCandidate, + ): boolean { + const {local: currLocal, neighbour: currNeighbour} = currCandiate; + const localPos = new Coordinate(currLocal.x, currLocal.y); + const neighbourPos = new Coordinate(currNeighbour.x, currNeighbour.y); + const distance = Coordinate.distance( + Coordinate.sum(localPos, delta), + neighbourPos, + ); + return ( + newCandidate.distance > distance - config.currentConnectionPreference + ); + } + + /** + * Returns the closest valid candidate connection, if one can be found. + * + * Valid neighbour connections are within the configured start radius, with a + * compatible type (input, output, etc) and connection check. + */ + private getConnectionCandidate( + draggingBlock: BlockSvg, + delta: Coordinate, + ): ConnectionCandidate | null { + const localConns = this.getLocalConnections(draggingBlock); + let radius = config.snapRadius; + let candidate = null; + + for (const conn of localConns) { + const {connection: neighbour, radius: rad} = conn.closest(radius, delta); + if (neighbour) { + candidate = { + local: conn, + neighbour: neighbour, + distance: rad, + }; + radius = rad; + } } - // Call drag enter/exit/over after wouldDeleteBlock is called in - // InsertionMarkerManager.update. - if (this.dragTarget_ !== oldDragTarget) { - oldDragTarget && oldDragTarget.onDragExit(this.draggingBlock_); - this.dragTarget_ && this.dragTarget_.onDragEnter(this.draggingBlock_); + return candidate; + } + + /** + * Returns all of the connections we might connect to blocks on the workspace. + * + * Includes any connections on the dragging block, and any last next + * connection on the stack (if one exists). + */ + private getLocalConnections(draggingBlock: BlockSvg): RenderedConnection[] { + const available = draggingBlock.getConnections_(false); + const lastOnStack = draggingBlock.lastConnectionInStack(true); + if (lastOnStack && lastOnStack !== draggingBlock.nextConnection) { + available.push(lastOnStack); } - this.dragTarget_ && this.dragTarget_.onDragOver(this.draggingBlock_); + return available; } /** @@ -216,6 +380,7 @@ export class BlockDragger implements IBlockDragger { dom.stopTextWidthCache(); blockAnimation.disconnectUiStop(); + this.connectionPreviewer.hidePreview(); const preventMove = !!this.dragTarget_ && @@ -298,15 +463,33 @@ export class BlockDragger implements IBlockDragger { */ protected updateBlockAfterMove_() { this.fireMoveEvent_(); - if (this.draggedConnectionManager_.wouldConnectBlock()) { + if (this.connectionCandidate) { // Applying connections also rerenders the relevant blocks. - this.draggedConnectionManager_.applyConnections(); + this.applyConnections(this.connectionCandidate); } else { this.draggingBlock_.queueRender(); } this.draggingBlock_.scheduleSnapAndBump(); } + private applyConnections(candidate: ConnectionCandidate) { + const {local, neighbour} = candidate; + local.connect(neighbour); + // TODO: We can remove this `rendered` check when we reconcile with v11. + if (this.draggingBlock_.rendered) { + const inferiorConnection = local.isSuperior() ? neighbour : local; + const rootBlock = this.draggingBlock_.getRootBlock(); + + finishQueuedRenders().then(() => { + blockAnimation.connectionUiEffect(inferiorConnection.getSourceBlock()); + // bringToFront is incredibly expensive. Delay until the next frame. + setTimeout(() => { + rootBlock.bringToFront(); + }, 0); + }); + } + } + /** Fire a UI event at the end of a block drag. */ protected fireDragEndEvent_() { const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))( @@ -415,14 +598,9 @@ export class BlockDragger implements IBlockDragger { * @returns A possibly empty list of insertion marker blocks. */ getInsertionMarkers(): BlockSvg[] { - // No insertion markers with the old style of dragged connection managers. - if ( - this.draggedConnectionManager_ && - this.draggedConnectionManager_.getInsertionMarkers - ) { - return this.draggedConnectionManager_.getInsertionMarkers(); - } - return []; + return this.workspace_ + .getAllBlocks() + .filter((block) => block.isInsertionMarker()); } } diff --git a/core/config.ts b/core/config.ts index ed253e9dd..a6642c266 100644 --- a/core/config.ts +++ b/core/config.ts @@ -48,6 +48,7 @@ export const config: Config = { * Maximum misalignment between connections for them to snap together. * This should be the same as the snap radius. * + * @deprecated v11 - This is no longer used. Use snapRadius instead. */ connectingSnapRadius: DEFAULT_SNAP_RADIUS, /** From 702eed42db91e73286986ae134db426f56aba852 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Thu, 25 Jan 2024 15:58:00 -0800 Subject: [PATCH 29/31] fix: highlighting connections in zelos, also highlight connections moving (#7795) * fix: remove zelos highlight override * feat: add isHighlighted to rendered connection * feat: add refreshing connection highlighting * chore: remove highlight and unhighlight connection APIs * chore: PR comments --- core/rendered_connection.ts | 12 +++++++-- core/renderers/common/drawer.ts | 30 +++++++++++++++++------ core/renderers/common/path_object.ts | 31 ++++++++++++++++++------ core/renderers/common/renderer.ts | 19 --------------- core/renderers/geras/drawer.ts | 1 + core/renderers/measurables/connection.ts | 9 ++++--- core/renderers/zelos/drawer.ts | 12 ++++----- core/renderers/zelos/renderer.ts | 8 ------ 8 files changed, 69 insertions(+), 53 deletions(-) diff --git a/core/rendered_connection.ts b/core/rendered_connection.ts index d26e34d44..f66b64005 100644 --- a/core/rendered_connection.ts +++ b/core/rendered_connection.ts @@ -36,6 +36,7 @@ export class RenderedConnection extends Connection { private readonly dbOpposite: ConnectionDB; private readonly offsetInBlock: Coordinate; private trackedState: TrackedState; + private highlighted: boolean = false; /** Connection this connection connects to. Null if not connected. */ override targetConnection: RenderedConnection | null = null; @@ -287,12 +288,19 @@ export class RenderedConnection extends Connection { /** Add highlighting around this connection. */ highlight() { - this.getSourceBlock().workspace.getRenderer().highlightConnection(this); + this.highlighted = true; + this.getSourceBlock().queueRender(); } /** Remove the highlighting around this connection. */ unhighlight() { - this.getSourceBlock().workspace.getRenderer().unhighlightConnection(this); + this.highlighted = false; + this.getSourceBlock().queueRender(); + } + + /** Returns true if this connection is highlighted, false otherwise. */ + isHighlighted(): boolean { + return this.highlighted; } /** diff --git a/core/renderers/common/drawer.ts b/core/renderers/common/drawer.ts index bda4303af..83732516c 100644 --- a/core/renderers/common/drawer.ts +++ b/core/renderers/common/drawer.ts @@ -22,7 +22,6 @@ import {isDynamicShape, isNotch, isPuzzleTab} from './constants.js'; import type {ConstantProvider, Notch, PuzzleTab} from './constants.js'; import type {RenderInfo} from './info.js'; import * as deprecation from '../../utils/deprecation.js'; -import type {RenderedConnection} from '../../rendered_connection.js'; import {ConnectionType} from '../../connection_type.js'; /** @@ -62,6 +61,7 @@ export class Drawer { draw() { this.drawOutline_(); this.drawInternals_(); + this.updateConnectionHighlights(); this.block_.pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); if (this.info_.RTL) { @@ -443,13 +443,29 @@ export class Drawer { } } - /** Returns a path to highlight the given connection. */ - drawConnectionHighlightPath(conn: RenderedConnection) { - const measurable = this.info_.getMeasureableForConnection(conn); - if (!measurable) { - throw new Error('Could not find measurable for connection'); - } + /** + * Updates the path object to reflect which connections on the block are + * highlighted. + */ + protected updateConnectionHighlights() { + for (const row of this.info_.rows) { + for (const elem of row.elements) { + if (!(elem instanceof Connection)) continue; + if (elem.highlighted) { + this.drawConnectionHighlightPath(elem); + } else { + this.block_.pathObject.removeConnectionHighlight?.( + elem.connectionModel, + ); + } + } + } + } + + /** Returns a path to highlight the given connection. */ + drawConnectionHighlightPath(measurable: Connection) { + const conn = measurable.connectionModel; let path = ''; if ( conn.type === ConnectionType.INPUT_VALUE || diff --git a/core/renderers/common/path_object.ts b/core/renderers/common/path_object.ts index 35e1f83c5..d5c0850a1 100644 --- a/core/renderers/common/path_object.ts +++ b/core/renderers/common/path_object.ts @@ -40,12 +40,11 @@ export class PathObject implements IPathObject { constants: ConstantProvider; style: BlockStyle; - /** - * Highlight paths associated with connections. - * - * @protected - */ - connectionHighlights = new WeakMap(); + /** Highlight paths associated with connections. */ + private connectionHighlights = new WeakMap(); + + /** Locations of connection highlights. */ + private highlightOffsets = new WeakMap(); /** * @param root The root SVG element. @@ -273,7 +272,13 @@ export class PathObject implements IPathObject { offset: Coordinate, rtl: boolean, ) { - if (this.connectionHighlights.has(connection)) return; + if (this.connectionHighlights.has(connection)) { + if (this.currentHighlightMatchesNew(connection, connectionPath, offset)) { + return; + } + this.removeConnectionHighlight(connection); + } + const highlight = dom.createSvgElement( Svg.PATH, { @@ -287,6 +292,18 @@ export class PathObject implements IPathObject { this.connectionHighlights.set(connection, highlight); } + private currentHighlightMatchesNew( + connection: RenderedConnection, + newPath: string, + newOffset: Coordinate, + ): boolean { + const currPath = this.connectionHighlights + .get(connection) + ?.getAttribute('d'); + const currOffset = this.highlightOffsets.get(connection); + return currPath === newPath && Coordinate.equals(currOffset, newOffset); + } + /** * Removes any highlight associated with the given connection, if it exists. */ diff --git a/core/renderers/common/renderer.ts b/core/renderers/common/renderer.ts index d171f8b09..3a53322dd 100644 --- a/core/renderers/common/renderer.ts +++ b/core/renderers/common/renderer.ts @@ -257,25 +257,6 @@ export class Renderer implements IRegistrable { return InsertionMarkerManager.PREVIEW_TYPE.INSERTION_MARKER; } - /** - * Visually highlights the given connection with a border, if it is not - * already highlighted. - */ - highlightConnection(conn: RenderedConnection): void { - const block = conn.getSourceBlock(); - - const info = this.makeRenderInfo_(block); - info.measure(); - const drawer = this.makeDrawer_(block, info); - - drawer.drawConnectionHighlightPath(conn); - } - - /** Visually unhighlights the given connection, if it is highlighted. */ - unhighlightConnection(conn: RenderedConnection): void { - conn.getSourceBlock().pathObject.removeConnectionHighlight?.(conn); - } - /** * Render the block. * diff --git a/core/renderers/geras/drawer.ts b/core/renderers/geras/drawer.ts index fd9a0795e..29bcbfab4 100644 --- a/core/renderers/geras/drawer.ts +++ b/core/renderers/geras/drawer.ts @@ -40,6 +40,7 @@ export class Drawer extends BaseDrawer { override draw() { this.drawOutline_(); this.drawInternals_(); + this.updateConnectionHighlights(); const pathObject = this.block_.pathObject as PathObject; pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); diff --git a/core/renderers/measurables/connection.ts b/core/renderers/measurables/connection.ts index e25215770..5744eaab4 100644 --- a/core/renderers/measurables/connection.ts +++ b/core/renderers/measurables/connection.ts @@ -20,6 +20,7 @@ import {Types} from './types.js'; export class Connection extends Measurable { shape: Shape; isDynamicShape: boolean; + highlighted: boolean; /** * @param constants The rendering constants provider. @@ -32,9 +33,11 @@ export class Connection extends Measurable { ) { super(constants); - this.shape = this.constants_.shapeFor(connectionModel); - - this.isDynamicShape = 'isDynamic' in this.shape && this.shape.isDynamic; this.type |= Types.CONNECTION; + + this.shape = this.constants_.shapeFor(connectionModel); + this.isDynamicShape = 'isDynamic' in this.shape && this.shape.isDynamic; + + this.highlighted = connectionModel.isHighlighted(); } } diff --git a/core/renderers/zelos/drawer.ts b/core/renderers/zelos/drawer.ts index 1f78047f0..009247aea 100644 --- a/core/renderers/zelos/drawer.ts +++ b/core/renderers/zelos/drawer.ts @@ -8,10 +8,10 @@ import type {BlockSvg} from '../../block_svg.js'; import {ConnectionType} from '../../connection_type.js'; -import {RenderedConnection} from '../../rendered_connection.js'; import * as svgPaths from '../../utils/svg_paths.js'; import type {BaseShape, DynamicShape, Notch} from '../common/constants.js'; import {Drawer as BaseDrawer} from '../common/drawer.js'; +import {Connection} from '../measurables/connection.js'; import type {InlineInput} from '../measurables/inline_input.js'; import {OutputConnection} from '../measurables/output_connection.js'; import type {Row} from '../measurables/row.js'; @@ -44,6 +44,7 @@ export class Drawer extends BaseDrawer { pathObject.beginDrawing(); this.drawOutline_(); this.drawInternals_(); + this.updateConnectionHighlights(); pathObject.setPath(this.outlinePath_ + '\n' + this.inlinePath_); if (this.info_.RTL) { @@ -236,17 +237,14 @@ export class Drawer extends BaseDrawer { } /** Returns a path to highlight the given connection. */ - drawConnectionHighlightPath(conn: RenderedConnection) { - const measurable = this.info_.getMeasureableForConnection(conn); - if (!measurable) { - throw new Error('Could not find measurable for connection'); - } + drawConnectionHighlightPath(measurable: Connection) { + const conn = measurable.connectionModel; if ( conn.type === ConnectionType.NEXT_STATEMENT || conn.type === ConnectionType.PREVIOUS_STATEMENT || (conn.type === ConnectionType.OUTPUT_VALUE && !measurable.isDynamicShape) ) { - super.drawConnectionHighlightPath(conn); + super.drawConnectionHighlightPath(measurable); return; } diff --git a/core/renderers/zelos/renderer.ts b/core/renderers/zelos/renderer.ts index a3bd5f70e..729c54f5f 100644 --- a/core/renderers/zelos/renderer.ts +++ b/core/renderers/zelos/renderer.ts @@ -7,7 +7,6 @@ // Former goog.module ID: Blockly.zelos.Renderer import type {BlockSvg} from '../../block_svg.js'; -import type {Connection} from '../../connection.js'; import {ConnectionType} from '../../connection_type.js'; import {InsertionMarkerManager} from '../../insertion_marker_manager.js'; import type {Marker} from '../../keyboard_nav/marker.js'; @@ -109,13 +108,6 @@ export class Renderer extends BaseRenderer { return this.constants_; } - override shouldHighlightConnection(conn: Connection) { - return ( - conn.type !== ConnectionType.INPUT_VALUE && - conn.type !== ConnectionType.OUTPUT_VALUE - ); - } - override getConnectionPreviewMethod( closest: RenderedConnection, local: RenderedConnection, From 018df03715602bf58ee42751b625762ddf6dd738 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 26 Jan 2024 14:11:47 -0800 Subject: [PATCH 30/31] chore: deprecate insertion marker manager and related methods (#7797) --- core/insertion_marker_manager.ts | 2 ++ core/renderers/common/renderer.ts | 10 ++++++++++ core/renderers/zelos/renderer.ts | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/core/insertion_marker_manager.ts b/core/insertion_marker_manager.ts index 69d3d2cb9..96c9327fc 100644 --- a/core/insertion_marker_manager.ts +++ b/core/insertion_marker_manager.ts @@ -56,6 +56,8 @@ const DUPLICATE_BLOCK_ERROR = * Class that controls updates to connections during drags. It is primarily * responsible for finding the closest eligible connection and highlighting or * unhighlighting it as needed during a drag. + * + * @deprecated v10 - Use an IConnectionPreviewer instead. */ export class InsertionMarkerManager { /** diff --git a/core/renderers/common/renderer.ts b/core/renderers/common/renderer.ts index 3a53322dd..15a958db4 100644 --- a/core/renderers/common/renderer.ts +++ b/core/renderers/common/renderer.ts @@ -26,6 +26,7 @@ import type {IPathObject} from './i_path_object.js'; import {RenderInfo} from './info.js'; import {MarkerSvg} from './marker_svg.js'; import {PathObject} from './path_object.js'; +import * as deprecation from '../../utils/deprecation.js'; /** * The base class for a block renderer. @@ -231,12 +232,21 @@ export class Renderer implements IRegistrable { * @param local The connection currently being dragged. * @param topBlock The block currently being dragged. * @returns The preview type to display. + * + * @deprecated v10 - This function is no longer respected. A custom + * IConnectionPreviewer may be able to fulfill the functionality. */ getConnectionPreviewMethod( closest: RenderedConnection, local: RenderedConnection, topBlock: BlockSvg, ): PreviewType { + deprecation.warn( + 'getConnectionPreviewMethod', + 'v10', + 'v12', + 'an IConnectionPreviewer, if it fulfills your use case.', + ); if ( local.type === ConnectionType.OUTPUT_VALUE || local.type === ConnectionType.PREVIOUS_STATEMENT diff --git a/core/renderers/zelos/renderer.ts b/core/renderers/zelos/renderer.ts index 729c54f5f..354a3f35a 100644 --- a/core/renderers/zelos/renderer.ts +++ b/core/renderers/zelos/renderer.ts @@ -22,6 +22,7 @@ import {Drawer} from './drawer.js'; import {RenderInfo} from './info.js'; import {MarkerSvg} from './marker_svg.js'; import {PathObject} from './path_object.js'; +import * as deprecation from '../../utils/deprecation.js'; /** * The zelos renderer. This renderer emulates Scratch-style and MakeCode-style @@ -108,11 +109,21 @@ export class Renderer extends BaseRenderer { return this.constants_; } + /** + * @deprecated v10 - This function is no longer respected. A custom + * IConnectionPreviewer may be able to fulfill the functionality. + */ override getConnectionPreviewMethod( closest: RenderedConnection, local: RenderedConnection, topBlock: BlockSvg, ) { + deprecation.warn( + 'getConnectionPreviewMethod', + 'v10', + 'v12', + 'an IConnectionPreviewer, if it fulfills your use case.', + ); if (local.type === ConnectionType.OUTPUT_VALUE) { if (!closest.isConnected()) { return InsertionMarkerManager.PREVIEW_TYPE.INPUT_OUTLINE; From 919778eb56924c2647662ab1c8110609c58dee68 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 26 Jan 2024 14:33:27 -0800 Subject: [PATCH 31/31] fix: have the connection previewer use the registry (#7800) --- core/block_dragger.ts | 11 ++++++----- core/blockly.ts | 2 ++ .../insertion_marker_previewer.ts | 7 +++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/core/block_dragger.ts b/core/block_dragger.ts index 57c77bb0d..1cab37053 100644 --- a/core/block_dragger.ts +++ b/core/block_dragger.ts @@ -31,7 +31,6 @@ import {hasBubble} from './interfaces/i_has_bubble.js'; import * as deprecation from './utils/deprecation.js'; import * as layers from './layers.js'; import {ConnectionType, IConnectionPreviewer} from './blockly.js'; -import {InsertionMarkerPreviewer} from './connection_previewers/insertion_marker_previewer.js'; import {RenderedConnection} from './rendered_connection.js'; import {config} from './config.js'; import {ComponentManager} from './component_manager.js'; @@ -86,12 +85,14 @@ export class BlockDragger implements IBlockDragger { */ constructor(block: BlockSvg, workspace: WorkspaceSvg) { this.draggingBlock_ = block; - - // TODO: have this access the registry instead. - this.connectionPreviewer = new InsertionMarkerPreviewer(block); - this.workspace_ = workspace; + const previewerConstructor = registry.getClassFromOptions( + registry.Type.CONNECTION_PREVIEWER, + this.workspace_.options, + ); + this.connectionPreviewer = new previewerConstructor!(block); + /** * The location of the top left corner of the dragging block at the * beginning of the drag in workspace coordinates. diff --git a/core/blockly.ts b/core/blockly.ts index 9abea068e..92d4a6740 100644 --- a/core/blockly.ts +++ b/core/blockly.ts @@ -128,6 +128,7 @@ import {Input} from './inputs/input.js'; import {inputTypes} from './inputs/input_types.js'; import * as inputs from './inputs.js'; import {InsertionMarkerManager} from './insertion_marker_manager.js'; +import {InsertionMarkerPreviewer} from './connection_previewers/insertion_marker_previewer.js'; import {IASTNodeLocation} from './interfaces/i_ast_node_location.js'; import {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js'; import {IASTNodeLocationWithBlock} from './interfaces/i_ast_node_location_with_block.js'; @@ -607,6 +608,7 @@ export {IMovable}; export {Input}; export {inputs}; export {InsertionMarkerManager}; +export {InsertionMarkerPreviewer}; export {IObservable, isObservable}; export {IPaster, isPaster}; export {IPositionable}; diff --git a/core/connection_previewers/insertion_marker_previewer.ts b/core/connection_previewers/insertion_marker_previewer.ts index 73f620dca..574fa8409 100644 --- a/core/connection_previewers/insertion_marker_previewer.ts +++ b/core/connection_previewers/insertion_marker_previewer.ts @@ -11,6 +11,7 @@ import {WorkspaceSvg} from '../workspace_svg.js'; import * as eventUtils from '../events/utils.js'; import * as constants from '../constants.js'; import * as renderManagement from '../render_management.js'; +import * as registry from '../registry.js'; /** * An error message to throw if the block created by createMarkerBlock_ is @@ -247,3 +248,9 @@ export class InsertionMarkerPreviewer implements IConnectionPreviewer { this.hidePreview(); } } + +registry.register( + registry.Type.CONNECTION_PREVIEWER, + registry.DEFAULT, + InsertionMarkerPreviewer, +);