From af5a300f47afe98beb3de9ea6417ff4337cd274a Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Thu, 4 Sep 2025 14:45:23 -0700 Subject: [PATCH 01/12] add .webp --- src/web/js/google-apis/picker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/js/google-apis/picker.js b/src/web/js/google-apis/picker.js index c9e890a5d..199f8c502 100644 --- a/src/web/js/google-apis/picker.js +++ b/src/web/js/google-apis/picker.js @@ -103,7 +103,7 @@ FilePicker.prototype.initOpen = function(picker) { * A Picker View which displays images users may load. */ var imageView = new picker.View(picker.ViewId.DOCS); - imageView.setMimeTypes("image/png,image/jpeg,image/jpg,image/gif"); + imageView.setMimeTypes("image/png,image/jpeg,image/jpg,image/gif,image/webp"); var allViews = { imageView: imageView, From 47699e7fd6a06390262dafe6e3cdf69ea2f664f9 Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Tue, 9 Sep 2025 16:22:30 -0700 Subject: [PATCH 02/12] Make a global for embed detection. Don't capture mod-S when embedded This makes it so save events properly propagate out of the webview and to the surrounding context. This is a good thing for VScode --- src/web/js/beforePyret.js | 3 +++ src/web/js/cpo-main.js | 16 +++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/web/js/beforePyret.js b/src/web/js/beforePyret.js index 7a1010095..2bcef2878 100644 --- a/src/web/js/beforePyret.js +++ b/src/web/js/beforePyret.js @@ -1460,6 +1460,7 @@ $(function() { let initialState = params["get"]["initialState"]; + window.PYRET_IS_EMBEDDED = false; if (typeof acquireVsCodeApi === "function") { window.MESSAGES = makeEvents({ CPO: CPO, @@ -1467,8 +1468,10 @@ $(function() { receivePort: window, initialState }); + window.PYRET_IS_EMBEDDED = true; } else if((window.parent && (window.parent !== window))) { window.MESSAGES = makeEvents({ CPO: CPO, sendPort: window.parent, receivePort: window, initialState }); + window.PYRET_IS_EMBEDDED = true; } }); diff --git a/src/web/js/cpo-main.js b/src/web/js/cpo-main.js index 3159e50b5..1cd6af4ac 100644 --- a/src/web/js/cpo-main.js +++ b/src/web/js/cpo-main.js @@ -666,11 +666,17 @@ // save // On Mac mod ends up mapping to command+s whereas on Windows and Linux it maps to ctrl+s. - Mousetrap.bindGlobal('mod+s', function(e) { - CPO.save(); - e.stopImmediatePropagation(); - e.preventDefault(); - }); + // Saving has a special condition: when embedded we want the Ctrl-S to + // propagate up. We could fire a special “save” event, but for contexts + // like VScode it is nice to have the “real” Cmd-S event fire to get + // good default behavior + if(!PYRET_IS_EMBEDDED) { + Mousetrap.bindGlobal('mod+s', function(e) { + CPO.save(); + e.stopImmediatePropagation(); + e.preventDefault(); + }); + } // resize, Toggle sizing of the editor window between 50% and last resize Mousetrap.bindGlobal('ctrl+m', function(e){ From 17dd4e16dbe05e22fd107d371fe5e923aebf9611 Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Tue, 9 Sep 2025 20:48:45 -0700 Subject: [PATCH 03/12] polyfill withResolvers to fix https://github.com/brownplt/code.pyret.org/issues/599 --- src/web/editor.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/web/editor.html b/src/web/editor.html index 952d5f965..6fcba526f 100644 --- a/src/web/editor.html +++ b/src/web/editor.html @@ -454,6 +454,18 @@

Announcements

var GIT_BRANCH = "{{ &GIT_BRANCH }}"; + From f8d41a1e33d57cd79ba03608600701a37ed68e75 Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Tue, 9 Sep 2025 21:41:58 -0700 Subject: [PATCH 04/12] Track last editor focused and refocus when window refocuses In desktop VScode, focus gets lost when Alt-Tabbing or switching tab views (unlike tabbing around a browser, which remembers) Do some focus-tracking for the various codemirror instances. This is likely not perfect yet, but it's a lot better Addresses https://github.com/jpolitz/pyret-parley-vscode/issues/13 --- src/web/js/beforePyret.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/web/js/beforePyret.js b/src/web/js/beforePyret.js index 2bcef2878..8cb40f983 100644 --- a/src/web/js/beforePyret.js +++ b/src/web/js/beforePyret.js @@ -152,6 +152,7 @@ $(function() { animationDiv = null; } } + let activeEditor = null; CPO.makeEditor = function(container, options) { var initial = ""; if (options.hasOwnProperty("initial")) { @@ -230,6 +231,9 @@ $(function() { cmOptions = merge(cmOptions, options.cmOptions || {}); var CM = CodeMirror.fromTextArea(textarea[0], cmOptions); + CM.on("focus", () => { + activeEditor = CM; + }); function firstLineIsNamespace() { const firstline = CM.getLine(0); @@ -1417,6 +1421,10 @@ $(function() { }); + window.addEventListener("focus", (e) => { + if(activeEditor) { activeEditor.focus(); } + }); + function makeEvent() { const handlers = []; function on(handler) { From 6bc74c52b2943b7235cbfb698dfb2d4c26857426 Mon Sep 17 00:00:00 2001 From: Shriram Krishnamurthi Date: Fri, 19 Sep 2025 17:23:03 -0400 Subject: [PATCH 05/12] =?UTF-8?q?toolbelt=20=E2=AD=A2=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 025c3e615..3f3af8596 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ everything in `.env` is just an environment variable if you really want to manage things yourself, but using Heroku tools makes sure you run like things do in production. -First, get the [Heroku toolbelt](https://toolbelt.heroku.com/). +First, get the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli). Then, copy `.env.example` to `.env`. If all you want to do is run Pyret code and test out the REPL, you only need to edit a few variables. If you want to From cad4c2ae3d8da1f5603a88c5959a6c9835221b9e Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Thu, 9 Oct 2025 14:00:49 -0700 Subject: [PATCH 06/12] selenium test to check that repeating decimals render correctly --- test/number.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/number.js diff --git a/test/number.js b/test/number.js new file mode 100644 index 000000000..a17196ae0 --- /dev/null +++ b/test/number.js @@ -0,0 +1,27 @@ +var assert = require("assert"); +var tester = require("../test-util/util.js"); +var webdriver = require("selenium-webdriver"); + +describe("Numbers at the REPL", function() { + beforeEach(tester.setup); + afterEach(tester.teardown); + + it("should render toggle-able numbers", function(done) { + this.timeout(80000); + var self = this; + this.browser.get(this.base + "/editor"); + this.browser.wait(function() { return tester.pyretLoaded(self.browser); }); + tester.evalPyret(this.browser, "1/7"); + console.log("Evaluated 1/7"); + this.browser.wait(function() { + return self.browser.findElements(webdriver.By.className("rationalRepeat")).then((els) => els.length > 0); + }); + this.browser.findElements(webdriver.By.className("rationalRepeat")).then(function(elements) { + elements[0].getAttribute("innerText").then(function(text) { + assert.equal(text, "142857"); + }); + }); + this.browser.call(done); + + }); +}); From 9c1824254dac9e0eb17c78b7302ea8fb6e07d4c7 Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Thu, 9 Oct 2025 14:01:02 -0700 Subject: [PATCH 07/12] stray log --- package-lock.json | 27 ++++++++++++++------------- package.json | 2 +- test/number.js | 1 - 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 63a2f49c6..e730b6586 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,7 +77,7 @@ "webpack": "^5.89.0" }, "devDependencies": { - "chromedriver": "^134.0.3", + "chromedriver": "^141.0.1", "selenium-webdriver": "^3.6.0", "webpack-cli": "^5.1.4" } @@ -8549,14 +8549,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", - "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, @@ -10031,15 +10031,15 @@ } }, "node_modules/chromedriver": { - "version": "134.0.3", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-134.0.3.tgz", - "integrity": "sha512-pRS401K3VRJMhFrK4NNceVE6xdoNOHC0P3sBylkLOb5p4MfrbXkjiCCipXoBD8jvui76t44FS5/taoVQNz9XUg==", + "version": "141.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-141.0.1.tgz", + "integrity": "sha512-BvBP/wlZDU/oDSQ7cbolKE2DI/PP2T2qDWN75+QiPkW5bUs/pd5uz4LYREl1fyoIerhLGhS0OSmMxpUfDbP4Tg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@testim/chrome-version": "^1.1.4", - "axios": "^1.7.4", + "axios": "^1.12.0", "compare-versions": "^6.1.0", "extract-zip": "^2.0.1", "proxy-agent": "^6.4.0", @@ -10050,7 +10050,7 @@ "chromedriver": "bin/chromedriver" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/cipher-base": { @@ -13016,15 +13016,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { diff --git a/package.json b/package.json index 8361871a6..bbe1f0bb2 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "author": "Joe Politz", "license": "Apache-2.0", "devDependencies": { - "chromedriver": "^134.0.3", + "chromedriver": "^141.0.1", "selenium-webdriver": "^3.6.0", "webpack-cli": "^5.1.4" } diff --git a/test/number.js b/test/number.js index a17196ae0..ed13cd6f0 100644 --- a/test/number.js +++ b/test/number.js @@ -12,7 +12,6 @@ describe("Numbers at the REPL", function() { this.browser.get(this.base + "/editor"); this.browser.wait(function() { return tester.pyretLoaded(self.browser); }); tester.evalPyret(this.browser, "1/7"); - console.log("Evaluated 1/7"); this.browser.wait(function() { return self.browser.findElements(webdriver.By.className("rationalRepeat")).then((els) => els.length > 0); }); From 622d150808498faf333e1778988ed9922f489f6d Mon Sep 17 00:00:00 2001 From: Ben Lerner Date: Thu, 16 Oct 2025 14:36:55 -0400 Subject: [PATCH 08/12] style fix: make chart tooltips be theme-aware counterpart to https://github.com/brownplt/pyret-lang/commit/1d83d54db18480444a68b507d07be9e6c46326f3 --- src/web/css/editor.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/web/css/editor.css b/src/web/css/editor.css index 2ee2388f9..560293a00 100644 --- a/src/web/css/editor.css +++ b/src/web/css/editor.css @@ -1612,6 +1612,18 @@ code div { #vg-tooltip-element { z-index: 15100; } +#vg-tooltip-element.visible { + font-size: inherit; +} + +#vg-tooltip-element.visible table tr td.key { + color: var(--repl-output); + padding-top: 0.5em; + padding-right: 0.5em; + padding-bottom: 0.5em; + /* Note: not setting background color, so that we get clearer rows */ + font-weight: var(--th-font-weight); +} .ui-dialog-titlebar { background-color: var(--ui-dialog-title-bg); From 81097b35cd21089974671e84b2335383200cc3e7 Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Tue, 16 Sep 2025 11:01:25 -0700 Subject: [PATCH 09/12] disable undo if in VScode mode --- src/web/js/beforePyret.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/web/js/beforePyret.js b/src/web/js/beforePyret.js index 8cb40f983..68f8fa1fd 100644 --- a/src/web/js/beforePyret.js +++ b/src/web/js/beforePyret.js @@ -197,8 +197,7 @@ $(function() { console.log("Using keymap: ", CodeMirror.keyMap.default, "macDefault: ", CodeMirror.keyMap.macDefault, "mac: ", mac); const modifier = mac ? "Cmd" : "Ctrl"; - var cmOptions = { - extraKeys: CodeMirror.normalizeKeyMap({ + const extraKeys = { "Shift-Enter": function(cm) { runFun(cm.getValue()); }, "Shift-Ctrl-Enter": function(cm) { runFun(cm.getValue()); }, "Tab": "indentAuto", @@ -211,7 +210,21 @@ $(function() { "Ctrl-Right": "goForwardToken", [`${modifier}-F`]: "findPersistent", [`${modifier}-/`]: "toggleComment", - }), + }; + if(window.PYRET_IN_VSCODE) { + // Disable undo and redo in vscode, since they mess with the host editor's undo/redo stack + // Oddly, it doesn't seem to work to add these to extraKeys; I have to + // override them in the default keymap + CodeMirror.keyMap.default[`${modifier}-Z`] = false; + CodeMirror.keyMap.default[`Shift-${modifier}-Z`] = false; + CodeMirror.keyMap.default[`${modifier}-Y`] = false; + // Ctrl-U is Undo within a range + CodeMirror.keyMap.default[`${modifier}-U`] = false; + } + + var cmOptions = { + keyMap: 'default', + extraKeys: CodeMirror.normalizeKeyMap(extraKeys), indentUnit: 2, tabSize: 2, viewportMargin: Infinity, @@ -1469,6 +1482,7 @@ $(function() { let initialState = params["get"]["initialState"]; window.PYRET_IS_EMBEDDED = false; + window.PYRET_IN_VSCODE = false; if (typeof acquireVsCodeApi === "function") { window.MESSAGES = makeEvents({ CPO: CPO, @@ -1477,6 +1491,7 @@ $(function() { initialState }); window.PYRET_IS_EMBEDDED = true; + window.PYRET_IN_VSCODE = true; } else if((window.parent && (window.parent !== window))) { window.MESSAGES = makeEvents({ CPO: CPO, sendPort: window.parent, receivePort: window, initialState }); From a492cc0e3a4149921a5d2f98839fa559c8ef5d72 Mon Sep 17 00:00:00 2001 From: Emmanuel Schanzer Date: Tue, 18 Nov 2025 18:55:46 -0500 Subject: [PATCH 10/12] force tbody elts to almost the viewport height (leave 250px for other chrome), and make their contents scrollable (#595) --- src/web/css/editor.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/web/css/editor.css b/src/web/css/editor.css index 560293a00..bdb9efcc4 100644 --- a/src/web/css/editor.css +++ b/src/web/css/editor.css @@ -2297,6 +2297,9 @@ table.pyret-table.pyret-matrix { table.pyret-table thead { box-shadow: 0px 2px 2px var(--shadow-color); } +/* Force tables to be the full height of the viewport, leaving 250px for other chrome */ +table.pyret-table tbody { max-height: calc(100vh - 250px); display: block; overflow: scroll; } +table.pyret-table tr { table-layout: fixed; display: table; width: 100%; } table.pyret-row th { box-shadow: 2px 0px 2px var(--shadow-color), -2px 0px 2px var(--shadow-color); border: none; From 3cf4b271c5dbc5cab8085c4c976fc443de01a2c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Schanzer Date: Tue, 18 Nov 2025 20:22:44 -0500 Subject: [PATCH 11/12] add shadows to hint at scrollable tables --- src/web/css/editor.css | 48 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/web/css/editor.css b/src/web/css/editor.css index bdb9efcc4..4d14b8b72 100644 --- a/src/web/css/editor.css +++ b/src/web/css/editor.css @@ -2294,12 +2294,56 @@ table.pyret-table.pyret-matrix { border-right-width: 2px; } + +table.pyret-table { width: 98%; } table.pyret-table thead { box-shadow: 0px 2px 2px var(--shadow-color); } -/* Force tables to be the full height of the viewport, leaving 250px for other chrome */ -table.pyret-table tbody { max-height: calc(100vh - 250px); display: block; overflow: scroll; } table.pyret-table tr { table-layout: fixed; display: table; width: 100%; } +/* Force tables to be the full height of the viewport, leaving 225px for other chrome */ +table.pyret-table tbody { + --bgRGB: 200, 210, 220; + --bg: rgb(var(--bgRGB)); + --bgTrans: rgba(var(--bgRGB), 0); + + --shadow: rgba(41, 50, 56, 0.5); + + max-height: calc(100vh - 225px); display: block; + overflow: auto; + + background: + /* Shadow Cover TOP */ + linear-gradient( + var(--bg) 30%, + var(--bgTrans) + ) center top, + + /* Shadow Cover BOTTOM */ + linear-gradient( + var(--bgTrans), + var(--bg) 70% + ) center bottom, + + /* Shadow TOP */ + radial-gradient( + farthest-side at 50% 0, + var(--shadow), + rgba(0, 0, 0, 0) + ) center top, + + /* Shadow BOTTOM */ + radial-gradient( + farthest-side at 50% 100%, + var(--shadow), + rgba(0, 0, 0, 0) + ) center bottom; + + background-repeat: no-repeat; + background-size: 100% 8px, 100% 5px, 100% 5px, 100% 8px; + background-attachment: local, local, scroll, scroll; +} + + table.pyret-row th { box-shadow: 2px 0px 2px var(--shadow-color), -2px 0px 2px var(--shadow-color); border: none; From 3b2ee00baeaf72df816d648da0c2a1fe3618f080 Mon Sep 17 00:00:00 2001 From: Joe Politz Date: Fri, 5 Dec 2025 09:40:24 -0800 Subject: [PATCH 12/12] Removing this special case. Did Google change something about CORS here? --- src/web/js/cpo-main.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/web/js/cpo-main.js b/src/web/js/cpo-main.js index 1cd6af4ac..33f023239 100644 --- a/src/web/js/cpo-main.js +++ b/src/web/js/cpo-main.js @@ -105,9 +105,11 @@ else if(window.IMAGE_PROXY_BYPASS) { return s; } + /* else if(a.hostname === "drive.google.com" && a.pathname === "/uc") { return s; } + */ else { return window.APP_BASE_URL + "/downloadImg?" + s; }