diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5531a8c3..eaf571ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,11 +10,13 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: Install Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af with: - node-version: 20.x + node-version: 22 cache: 'npm' - name: Install dependencies run: npm ci @@ -25,7 +27,7 @@ jobs: - name: Build standalone run: npm run build-standalone-prod - name: Upload standalone artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b with: name: standalone path: dist/standalone.html diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a9d815c5..65088344 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,11 +5,6 @@ on: push: branches: [master] -permissions: - contents: read - pages: write - id-token: write - concurrency: group: "deploy" cancel-in-progress: true @@ -19,14 +14,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: Setup GitHub Pages id: pages - uses: actions/configure-pages@v4 + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b - name: Install Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af with: - node-version: 20.x + node-version: 22 cache: 'npm' - name: Install dependencies run: npm ci @@ -44,7 +41,7 @@ jobs: npm run build-standalone-prod cp dist/standalone.html web - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa with: path: ./web/ @@ -52,9 +49,12 @@ jobs: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} + permissions: + pages: write + id-token: write runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e diff --git a/.github/workflows/generate-electron-binaries.yml b/.github/workflows/generate-electron-binaries.yml index 8e53d39b..7dc5dc7b 100644 --- a/.github/workflows/generate-electron-binaries.yml +++ b/.github/workflows/generate-electron-binaries.yml @@ -15,10 +15,12 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - node-version: 20.x + persist-credentials: false + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af + with: + node-version: 22 - name: Install dependencies run: | cd electron-bin @@ -31,7 +33,7 @@ jobs: node generate-macos.js - name: Upload macOS if: runner.os == 'macOS' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b with: name: electron-macos path: electron-bin/temp/macos/*.zip @@ -46,13 +48,13 @@ jobs: node generate-windows.js - name: Upload Windows if: runner.os == 'Windows' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b with: name: electron-windows path: electron-bin/temp/windows/*.zip - name: Upload Windows Crossbuild if: runner.os == 'Linux' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b with: name: electron-windows-crossbuild path: electron-bin/temp/windows/*.zip diff --git a/.github/workflows/wkwebview-build.yml b/.github/workflows/wkwebview-build.yml index 6afe635b..66641e38 100644 --- a/.github/workflows/wkwebview-build.yml +++ b/.github/workflows/wkwebview-build.yml @@ -11,7 +11,9 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: Build run: | cd wkwebview diff --git a/electron-bin/package-lock.json b/electron-bin/package-lock.json index 6d6fbda8..5e4f627a 100644 --- a/electron-bin/package-lock.json +++ b/electron-bin/package-lock.json @@ -474,9 +474,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1839,9 +1839,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", diff --git a/package-lock.json b/package-lock.json index e6bab656..be253b22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,17 +9,17 @@ "version": "3.0.0", "license": "MPL-2.0", "dependencies": { - "@fiahfy/icns": "0.0.7", + "@fiahfy/icns": "^0.0.7", "@turbowarp/json": "^0.1.1", "@turbowarp/jszip": "^3.11.0", - "@turbowarp/sbdl": "^4.0.1", - "cross-fetch": "^4.0.0", + "@turbowarp/sbdl": "^5.0.1", + "cross-fetch": "^4.1.0", "sha.js": "^2.4.11" }, "devDependencies": { "@babel/core": "^7.16.5", "@babel/preset-env": "^7.16.5", - "@turbowarp/scratch-storage": "^0.0.202403251715", + "@turbowarp/scratch-storage": "^0.0.202502192258", "@turbowarp/scratch-svg-renderer": "^1.0.0-202401111326-62c0f26", "babel-jest": "^27.4.5", "babel-loader": "^8.2.3", @@ -2375,32 +2375,23 @@ } }, "node_modules/@turbowarp/sbdl": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@turbowarp/sbdl/-/sbdl-4.0.1.tgz", - "integrity": "sha512-S4pCot6YqdgEAh3cjIXxPE4cTSOVHZC+Ru7CkdDrdXn32j9Nr0lhGUEeHejnnO6C2anPtR+n5PMPhl2Hza0t/g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@turbowarp/sbdl/-/sbdl-5.0.1.tgz", + "integrity": "sha512-tcWfKJNbAHMW6iIYzDo+Fb0AV3dwdCVWgkfwJ5cQbyAm+r7gMRJHzk5gRQhJsMYRpqBFu5TKP10cEQs049oDTg==", "license": "MIT", "dependencies": { "@turbowarp/json": "^0.1.2", - "cross-fetch": "^3.1.5", + "cross-fetch": "^4.1.0", "jszip": "^3.10.1" }, "bin": { "sbdl": "src/cli.js" } }, - "node_modules/@turbowarp/sbdl/node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, "node_modules/@turbowarp/scratch-storage": { - "version": "0.0.202403251715", - "resolved": "https://registry.npmjs.org/@turbowarp/scratch-storage/-/scratch-storage-0.0.202403251715.tgz", - "integrity": "sha512-thb0e1MlgddSLJ63WRUNb4QhDhA7gE8t1Fmoi2NbbERxdbJ5yMyRa95JqkJUG2UqzqBnNnhiXYCKKRdVzpiizA==", + "version": "0.0.202502192258", + "resolved": "https://registry.npmjs.org/@turbowarp/scratch-storage/-/scratch-storage-0.0.202502192258.tgz", + "integrity": "sha512-74NnOz0THySIaZLydya3M6zuIedjUcMlbxY8xNc/yH+6bGGmP5S7WyhwHd/8LAgOslT+R6EaIXppWb0eyktUqg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3730,17 +3721,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bl": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", @@ -4942,12 +4922,12 @@ } }, "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", "license": "MIT", "dependencies": { - "node-fetch": "^2.6.12" + "node-fetch": "^2.7.0" } }, "node_modules/cross-spawn": { @@ -6810,14 +6790,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -7102,21 +7074,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -10248,14 +10205,6 @@ "duplexer2": "^0.1.2" } }, - "node_modules/nan": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", - "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -14882,26 +14831,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack-chokidar2/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/watchpack-chokidar2/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -15547,26 +15476,6 @@ "node": ">=6" } }, - "node_modules/webpack-dev-server/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/webpack-dev-server/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", diff --git a/package.json b/package.json index 5d21dbc5..afb5041c 100644 --- a/package.json +++ b/package.json @@ -34,17 +34,17 @@ "funding": "https://github.com/sponsors/GarboMuffin", "license": "MPL-2.0", "dependencies": { - "@fiahfy/icns": "0.0.7", + "@fiahfy/icns": "^0.0.7", "@turbowarp/json": "^0.1.1", "@turbowarp/jszip": "^3.11.0", - "@turbowarp/sbdl": "^4.0.1", - "cross-fetch": "^4.0.0", + "@turbowarp/sbdl": "^5.0.1", + "cross-fetch": "^4.1.0", "sha.js": "^2.4.11" }, "devDependencies": { "@babel/core": "^7.16.5", "@babel/preset-env": "^7.16.5", - "@turbowarp/scratch-storage": "^0.0.202403251715", + "@turbowarp/scratch-storage": "^0.0.202502192258", "@turbowarp/scratch-svg-renderer": "^1.0.0-202401111326-62c0f26", "babel-jest": "^27.4.5", "babel-loader": "^8.2.3", diff --git a/src/addons/pause.js b/src/addons/pause.js index 0f017dc0..8d03804f 100644 --- a/src/addons/pause.js +++ b/src/addons/pause.js @@ -36,7 +36,8 @@ export default function ({ scaffolding }) { // Immediately emit project stop // Scratch will do this automatically, but there may be a slight delay. - vm.runtime.emit("PROJECT_RUN_STOP"); + vm.runtime.emit('PROJECT_RUN_STOP'); + vm.runtime.emit('RUNTIME_PAUSED'); } else { audioContextStateChange = audioContextStateChange.then(() => { return vm.runtime.audioEngine.audioContext.resume(); @@ -53,6 +54,9 @@ export default function ({ scaffolding }) { stackFrame.executionContext.timer.startTime += dt; } // Compiler state is stored differently + if (thread.compatibilityStackFrame && thread.compatibilityStackFrame.timer) { + thread.compatibilityStackFrame.timer.startTime += now - pauseState.pauseTime; + } if (thread.timer) { const dt = now - pauseState.pauseTime; thread.timer.startTime += dt; @@ -61,9 +65,9 @@ export default function ({ scaffolding }) { } } pausedThreadState = new WeakMap(); - } - vm.emit('P4_PAUSE', paused); + vm.runtime.emit('RUNTIME_UNPAUSED'); + } }; const ensurePausedThreadIsStillPaused = (thread) => { diff --git a/src/locales/sl.json b/src/locales/sl.json index 4796ab50..5d72b884 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -16,7 +16,13 @@ "accentColor": "Barva poudarkov (aktivni gumbi, polje za vnos odgovora, kontekstni meniji)", "advancedOptions": "Napredne možnosti", "advancedSummary": "Teh verjetno nočete spremeniti. (Kliknite, da odprete)", + "application-linux-arm32": "Aplikacija {type} za Linux (ARM, 32-bitna)", + "application-linux-arm64": "Aplikacija {type} za Linux (ARM, 64-bitna)", + "application-linux64": "Aplikacija {type} za Linux (64-bitna)", "application-mac": "Aplikacija {type} za macOS", + "application-win-arm": "Aplikacija {type} za Windows (ARM)", + "application-win32": "Aplikacija {type} za Windows (32-bitna)", + "application-win64": "Aplikacija {type} za Windows (64-bitna)", "applicationSettings": "Nastavitve aplikacije", "automaticallyCenter": "Samodejno določi sredino", "autoplay": "Samodejno začni namesto prikazovanja velike zelene zastavice", @@ -55,6 +61,7 @@ "icon": "Ikona strani", "import": "Uvozi nastavitve", "infiniteClones": "Neskončno klonov", + "initalWindowSize": "Začetna velikost okna", "interaction": "Vhod", "interpolation": "Interpolacija", "learnMore": "Več informacij", @@ -96,12 +103,15 @@ "startFullscreen": "Začni v celozaslonskem načinu", "startMaximized": "Začni v razširjenem oknu", "startWindow": "Začni v oknu", + "steamworksError": "Pokaži napako in zapri", + "steamworksWarning": "Pokaži opozorilo in nadaljuj", "storedWarning": "Opomba: V tem projektu so shranjene nastavitve, ki lahko povozijo te nastavitve.", "stretch": "Raztegni oder, da napolni zaslon, brez spreminjanja dejanske velikosti (poskusno)", "turbo": "Turbo način", "username": "Uporabniško ime (vsak \"#\" bo zamenjan z naključno številko)", "variableColor": "Barva spremenljivk", "version": "Različica", + "versionHelp": "Nastavitev različice ni nujna in ne vpliva na delovanje projekta. Odvisno od okolja je lahko prikazana na različnih mestih, kot so okna z lastnostmi. Različica naj bo v obliki X.Y.Z.", "zip": "Zip (priporočeno za spletne strani)", "zip-one-asset": "Stisnjena mapa, vse slike in zvoki v eni datoteki (ni priporočeno)" }, @@ -120,6 +130,7 @@ "error": "Napaka", "errorMessage": "Sporočilo: {error}", "feedback": "Povratne informacije", + "importingInterface": "Nalaganje možnosti...", "networkError": "Prenašanje {url} ni uspelo. Prepričajte se, da ste povezani z internetom, in poskusite izključiti vse razširitve brskalnika.", "outdated": "Ta različica Packagerja je zastarela. Prosimo, da ponovno naložite stran in poskusite znova.", "privacy": "Politika zasebnosti", @@ -133,6 +144,7 @@ }, "progress": { "compressingProject": "Stiskanje projekta", + "downloadingExtensions": "Prenašanje razširitev po meri", "loadingAssets": "Prenašanje videzov in zvokov ({complete}/{total})", "loadingLargeAsset": "Prenašam {thing}", "loadingProjectData": "Prenašanje podatkov projekta", diff --git a/src/locales/zh-cn.json b/src/locales/zh-cn.json index ceca6b7d..6b60b625 100644 --- a/src/locales/zh-cn.json +++ b/src/locales/zh-cn.json @@ -120,7 +120,7 @@ "startFullscreen": "以全屏模式启动", "startMaximized": "以最大化窗口模式启动", "startWindow": "以窗口模式启动", - "steamworksAvailable": "此作品使用了 Steamworks 扩展。您可以在 Steamworks 找到您游戏的 App ID,或者使用 {n} 来通过 Steamworks demo 测试。", + "steamworksAvailable": "此作品使用了 Steamworks 扩展。您可以在 Steamworks 找到您游戏的 App ID,或者使用 {n} 来测试 Steamworks 测试版游戏。", "steamworksDocumentation": "查看扩展文档以了解详情", "steamworksError": "展示错误并退出", "steamworksExtension": "Steamworks 扩展", diff --git a/src/packager/download-project.js b/src/packager/download-project.js index 70635c96..0e4db2cb 100644 --- a/src/packager/download-project.js +++ b/src/packager/download-project.js @@ -66,7 +66,33 @@ const mutateScratch3InPlace = (projectData) => { } }; + const disableNonsenseCloudVariables = (projectData) => { + const DISABLE_CLOUD_VARIABLES = [ + // The "original" Sprunki project includes a cloud variable presumably used to detect who + // clicked on the report button. That seems like a Scratch community guidelines violation but + // that's not our job to enforce. This affects us because these games are very popular and + // create thousands of unnecessary concurrent cloud variable connections for a feature that + // can't work because there is no report button to click on. + '☁ potential reporters' + ]; + + // I want a more general solution here that automatically disables all unused cloud variables, + // but making that work in the presence of various unknown extensions seems non-trivial. + + const stage = projectData.targets.find((i) => i.isStage); + if (stage) { + for (const variable of Object.values(stage.variables)) { + // variable is [name, value, isCloud] + if (variable[2] && DISABLE_CLOUD_VARIABLES.includes(variable[0])) { + variable[2] = false; + } + } + } + }; + + // Order matters -- check for implied cloud variables before disabling some of them. makeImpliedCloudVariables(projectData); + disableNonsenseCloudVariables(projectData); optimizeSb3Json(projectData); }; diff --git a/src/packager/large-assets.js b/src/packager/large-assets.js index f9bf51fe..6b736b39 100644 --- a/src/packager/large-assets.js +++ b/src/packager/large-assets.js @@ -77,9 +77,9 @@ export default { estimatedSize: 93932512 }, 'webview-mac': { - src: externalFile('WebView-macos-5.zip'), - sha256: 'b5636571cd9be2aae2f6dac1ab090fdf829c8fdfe91f462cc2feb2d324705f9f', - estimatedSize: 3425601 + src: externalFile('WebView-macos-7.zip'), + sha256: 'fef0603a17df6dd976eb2aeb704aaec6d2666455089fbf3398becfaf5b29448b', + estimatedSize: 3530149 }, 'steamworks.js': { src: externalFile('steamworks.js-0.3.2.zip'), diff --git a/src/packager/packager.js b/src/packager/packager.js index 606034da..f603bdee 100644 --- a/src/packager/packager.js +++ b/src/packager/packager.js @@ -147,11 +147,17 @@ const CFBundleShortVersionString = 'CFBundleShortVersionString'; // https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype const LSApplicationCategoryType = 'LSApplicationCategoryType'; -const generateMacReadme = (options) => `When you try to double click on the app to run it, you will probably see this warning: -"${options.app.packageName} cannot be opened because the developer cannot be verified." -This is normal. Press cancel. +const generateMacReadme = (options) => `Due to macOS restrictions, running this app requires a few manual steps. -To run the app: +To run the app on macOS 15 and later: +1) Double click on the app file (${options.app.packageName} in the same folder as this document), then press "Done" when the warning appears +2) Open macOS System Settings +3) Go to the "Privacy & Security" section +4) Scroll to the bottom +5) By "${options.app.packageName} was blocked to protect your Mac", press "Open Anyway" +6) In the prompt that appears, press "Open Anyway" + +To run the app on macOS 14 and earlier: 1) Control+click on the app file (${options.app.packageName} in the same folder as this document) and select "Open". 2) If a warning appears, select "Open" if it's an option. 3) If a warning appears but "Open" isn't an option, press "Cancel" and repeat from step 1. @@ -1079,7 +1085,7 @@ cd "$(dirname "$0")" }; xhr.onerror = () => { if (location.protocol === 'file:') { - reject(new Error('Zip environment must be used from a website, not from a file URL.')); + reject(new Error('Zip environment must be used on a website, not on a local file. To fix this error, use the "Plain HTML" environment instead.')); } else { reject(new Error('Request to load project data failed.')); } @@ -1495,15 +1501,21 @@ cd "$(dirname "$0")" pauseButton.addEventListener('click', () => { vm.setPaused(!isPaused); }); - const updatePause = (_isPaused) => { - isPaused = _isPaused; + const updatePause = () => { if (isPaused) { pauseButton.src = 'data:image/svg+xml,' + encodeURIComponent(''); } else { pauseButton.src = 'data:image/svg+xml,' + encodeURIComponent(''); } - } - vm.on('P4_PAUSE', updatePause); + }; + vm.runtime.on('RUNTIME_PAUSED', () => { + isPaused = true; + updatePause(); + }); + vm.runtime.on('RUNTIME_UNPAUSED', () => { + isPaused = false; + updatePause(); + }); updatePause(); scaffolding.addControlButton({ element: pauseButton, diff --git a/src/scaffolding/monitor.js b/src/scaffolding/monitor.js index b32f3480..74cb588c 100644 --- a/src/scaffolding/monitor.js +++ b/src/scaffolding/monitor.js @@ -211,12 +211,14 @@ class VariableMonitor extends Monitor { const ROW_HEIGHT = 24; class Row { + /** @param {ListMonitor} monitor */ constructor (monitor) { + /** @type {ListMonitor} */ this.monitor = monitor; this.index = -1; this.value = ''; - this.locked = false; + this.isFocused = false; this.root = document.createElement('label'); this.root.className = styles.monitorRowRoot; @@ -230,14 +232,15 @@ class Row { this.editable = this.monitor.editable; if (this.editable) { this.valueInner = document.createElement('input'); - this.valueInner.tabIndex = -1; this.valueInner.className = styles.monitorRowValueInner; - this.valueInner.readOnly = true; - this.valueInner.addEventListener('click', this._onclickinput.bind(this)); - this.valueInner.addEventListener('blur', this._onblurinput.bind(this)); - this.valueInner.addEventListener('keypress', this._onkeypressinput.bind(this)); - this.valueInner.addEventListener('keydown', this._onkeypressdown.bind(this)); - this.valueInner.addEventListener('contextmenu', this._oncontextmenu.bind(this)); + // We can't mark the input as readonly by default and then allow editing only + // when clicking on it as iOS will not show the keyboard. Thus we need the input + // to be always editable, so we need to handle focus events as generally as + // possible. + this.valueInner.addEventListener('focusin', this._onfocusin.bind(this)); + this.valueInner.addEventListener('focusout', this._onfocusout.bind(this)); + this.valueInner.addEventListener('keypress', this._onkeypress.bind(this)); + this.valueInner.addEventListener('keydown', this._onkeydown.bind(this)); this.valueInner.addEventListener('input', this._oninput.bind(this)); this.valueOuter.appendChild(this.valueInner); @@ -250,21 +253,25 @@ class Row { this.valueInner = document.createElement('div'); this.valueInner.className = styles.monitorRowValueInner; this.valueOuter.appendChild(this.valueInner); - this.valueInner.addEventListener('contextmenu', this._oncontextmenuuneditable.bind(this)); } + this.valueInner.addEventListener('contextmenu', this._oncontextmenu.bind(this)); + this.root.appendChild(this.indexEl); this.root.appendChild(this.valueOuter); } - _onclickinput () { - this.valueInner.focus(); - if (this.locked) { + isLocked () { + return this.isFocused; + } + + _onfocusin () { + if (this.isFocused) { return; } + + this.isFocused = true; this.valueInner.select(); - this.valueInner.readOnly = false; - this.locked = true; this.root.classList.add(styles.monitorRowValueEditing); this.addNewValue = false; @@ -272,21 +279,27 @@ class Row { this.valueWasChanged = false; } - _onblurinput () { - if (!this.locked) { + _onfocusout () { + if (!this.isFocused) { return; } - this.unfocus(); + // Store our new cached value, otherwise if we get reused for a different row with + // the same value that the old row had before editing, setValue() will keep + // displaying the incorrect edited value instead of the actual value. + this.value = this.valueInner.value; + + this.isFocused = false; + this.root.classList.remove(styles.monitorRowValueEditing); if (this.deleteValue) { const value = [...this.monitor.value]; value.splice(this.index, 1); this.monitor.setValue(value); - this.monitor.tryToFocusRow(Math.min(value.length - 1, this.index)) + this.monitor.tryToFocusRow(Math.min(value.length - 1, this.index)); } else if (this.valueWasChanged || this.addNewValue) { const value = [...this.monitor.value]; - value[this.index] = this.valueInner.value; + value[this.index] = this.value; if (this.addNewValue) { value.splice(this.index + 1, 0, ''); } @@ -301,14 +314,14 @@ class Row { this.valueWasChanged = true; } - _onkeypressinput (e) { + _onkeypress (e) { if (e.key === 'Enter') { this.addNewValue = true; this.valueInner.blur(); } } - _onkeypressdown (e) { + _onkeydown (e) { if (e.key === 'Escape') { this.valueInner.blur(); } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'Tab') { @@ -332,20 +345,16 @@ class Row { } _oncontextmenu (e) { - if (this.locked) { - // Open native context menu instead of custom list one when editing + if (this.editable) { + // Show the native text editing context menu instead of our custom one. e.stopPropagation(); } else { - // Right clicking should not focus and highlight input - e.preventDefault(); - } - } - - _oncontextmenuuneditable (e) { - // When row has been highlighted, eg. by triple click, open native context menu instead of custom - const selection = getSelection(); - if (this.valueInner.contains(selection.anchorNode) && !selection.isCollapsed) { - e.stopPropagation(); + // When row has been highlighted, eg. by triple click, open native context menu instead of custom + // This allows people to copy and paste without needing to know ctrl+c. + const selection = getSelection(); + if (this.valueInner.contains(selection.anchorNode) && !selection.isCollapsed) { + e.stopPropagation(); + } } } @@ -359,7 +368,7 @@ class Row { } setValue (value) { - if (this.value !== value && !this.locked) { + if (this.value !== value && !this.isFocused) { this.value = value; if (this.editable) { this.valueInner.value = value; @@ -370,17 +379,14 @@ class Row { } focus () { - this.valueInner.click(); - if (document.activeElement !== this.valueInner) { - setTimeout(() => this.valueInner.click()); + if (!this.isFocused) { + this.valueInner.focus(); } } unfocus () { - if (this.locked) { - this.locked = false; - this.valueInner.readOnly = true; - this.root.classList.remove(styles.monitorRowValueEditing); + if (this.isFocused) { + this.valueInner.blur(); } } } @@ -390,7 +396,9 @@ class ListMonitor extends Monitor { super(parent, monitor); this.editable = parent.editableLists; + /** @type {Map} */ this.rows = new Map(); + /** @type {Row[]} */ this.cachedRows = []; this.scrollTop = 0; this.oldLength = -1; @@ -586,7 +594,7 @@ class ListMonitor extends Monitor { for (const index of this.rows.keys()) { if (index < startIndex || index > endIndex) { const row = this.rows.get(index); - if (!row.locked || index >= value.length) { + if (!row.isLocked() || index >= value.length) { row.unfocus(); row.root.remove(); this.rows.delete(index); diff --git a/wkwebview/WebView/ViewController.swift b/wkwebview/WebView/ViewController.swift index 6b362297..c33f1fa7 100644 --- a/wkwebview/WebView/ViewController.swift +++ b/wkwebview/WebView/ViewController.swift @@ -1,5 +1,5 @@ import Cocoa -import WebKit +@preconcurrency import WebKit class ViewController: NSViewController, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler { @IBOutlet var webView: WKWebView! diff --git a/wkwebview/WebView/WindowController.swift b/wkwebview/WebView/WindowController.swift index b6890175..1a373402 100644 --- a/wkwebview/WebView/WindowController.swift +++ b/wkwebview/WebView/WindowController.swift @@ -5,4 +5,8 @@ class WindowController : NSWindowController { super.init(coder: coder) shouldCascadeWindows = true } + + override func keyDown(with event: NSEvent) { +// Don't call super.keyDown so that it won't play the beeping noise. + } } diff --git a/wkwebview/WebView/index.html b/wkwebview/WebView/index.html index 27bbb3dd..e8a63dd1 100644 --- a/wkwebview/WebView/index.html +++ b/wkwebview/WebView/index.html @@ -5,15 +5,25 @@ +

It works!

+ + +