diff --git a/README.md b/README.md index 025c3e61..3f3af859 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 diff --git a/package-lock.json b/package-lock.json index 63a2f49c..e730b658 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 8361871a..bbe1f0bb 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/src/web/css/editor.css b/src/web/css/editor.css index 2ee2388f..4d14b8b7 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); @@ -2282,9 +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); } +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; diff --git a/src/web/editor.html b/src/web/editor.html index 952d5f96..6fcba526 100644 --- a/src/web/editor.html +++ b/src/web/editor.html @@ -454,6 +454,18 @@

Announcements

var GIT_BRANCH = "{{ &GIT_BRANCH }}"; + diff --git a/src/web/js/beforePyret.js b/src/web/js/beforePyret.js index 7a101009..68f8fa1f 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")) { @@ -196,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", @@ -210,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, @@ -230,6 +244,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 +1434,10 @@ $(function() { }); + window.addEventListener("focus", (e) => { + if(activeEditor) { activeEditor.focus(); } + }); + function makeEvent() { const handlers = []; function on(handler) { @@ -1460,6 +1481,8 @@ $(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, @@ -1467,8 +1490,11 @@ $(function() { receivePort: window, 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 }); + window.PYRET_IS_EMBEDDED = true; } }); diff --git a/src/web/js/cpo-main.js b/src/web/js/cpo-main.js index 3159e50b..33f02323 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; } @@ -666,11 +668,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){ diff --git a/src/web/js/google-apis/picker.js b/src/web/js/google-apis/picker.js index c9e890a5..199f8c50 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, diff --git a/test/number.js b/test/number.js new file mode 100644 index 00000000..ed13cd6f --- /dev/null +++ b/test/number.js @@ -0,0 +1,26 @@ +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"); + 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); + + }); +});