diff --git a/Project.toml b/Project.toml index 9ca0b74e0c..11be615268 100644 --- a/Project.toml +++ b/Project.toml @@ -2,7 +2,7 @@ name = "Pluto" uuid = "c3e4b0f8-55cb-11ea-2926-15256bba5781" license = "MIT" authors = ["Fons van der Plas ", "Mikołaj Bochenski "] -version = "0.8.0" +version = "0.8.1" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/README.md b/README.md index 0cf6d0689c..df6b4ec8be 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ We are happy to say that Pluto.jl runs smoothly for most users, and is **ready t That being said, the Pluto project is an ambition to [_rethink what a programming environment should be_](http://worrydream.com/#!/LearnableProgramming). We believe that scientific programming can be a lot simpler. Not by adding more buttons to a text editor — by giving space to creative thought, and automating the rest. -If you feel the same, give Pluto a try! We would love to hear your what you think. 😊 +If you feel the same, give Pluto a try! We would love to hear what you think. 😊 feedback screencap diff --git a/assets/editor.html b/assets/editor.html index cdbe0f2ae3..41c6a5df0f 100644 --- a/assets/editor.html +++ b/assets/editor.html @@ -267,8 +267,12 @@ position: relative; } - body.loading>main>preamble>button { - opacity: 0%; + main>preamble>button { + display: none; + } + + body.code-differs>main>preamble>button { + display: block; } cell { @@ -360,8 +364,8 @@ pointer-events: none; } - button.runall>span::after { - content: "Run all ▶▶"; + button.runallchanged>span::after { + content: "Submit all changes ▶▶"; } button.addcell>span::after { @@ -644,7 +648,7 @@

- + diff --git a/assets/editor.js b/assets/editor.js index 88f544286b..45526c3bd1 100644 --- a/assets/editor.js +++ b/assets/editor.js @@ -27,8 +27,8 @@ function updateRemoteNotebooks(list) { /* DOM THINGIES */ -document.querySelector("preamble>button.runall").onclick = (e) => { - requestRunAllRemoteCells() +document.querySelector("preamble>button.runallchanged").onclick = (e) => { + requestRunAllChangedRemoteCells() } cellTemplate = document.querySelector("#celltemplate").content.firstElementChild @@ -88,7 +88,7 @@ window.localCells = {} window.codeMirrors = {} function createCodeMirrorInsideCell(cellNode, code) { - var editor = CodeMirror((elt) => { + var cm = CodeMirror((elt) => { cellNode.querySelector("cellinput").appendChild(elt) }, { value: code, @@ -107,10 +107,10 @@ function createCodeMirrorInsideCell(cellNode, code) { matchBrackets: true, }); - window.codeMirrors[cellNode.id] = editor - //editor.setOption("readOnly", true); + window.codeMirrors[cellNode.id] = cm + //cm.setOption("readOnly", true); - editor.setOption("extraKeys", { + cm.setOption("extraKeys", { "Ctrl-Enter": () => requestChangeRemoteCell(cellNode.id), "Shift-Enter": () => { requestNewRemoteCell(indexOfLocalCell(cellNode) + 1) @@ -127,16 +127,15 @@ function createCodeMirrorInsideCell(cellNode, code) { "Tab": onTabKey, }); - editor.on("change", (cm, change) => { + cm.on("change", (cm, change) => { // TODO: optimise - if (cm.getValue() != cellNode.remoteCode) { - cellNode.classList.add("code-differs") - } else { - cellNode.classList.remove("code-differs") - } + const differs = cm.getValue() != cellNode.remoteCode + + cellNode.classList.toggle("code-differs", differs) + updateAnyCodeDiffers() }) - editor.on("cursorActivity", (cm) => { + cm.on("cursorActivity", (cm) => { const token = cm.getTokenAt(cm.getCursor()) if (token.type != null && token.type != "string") { @@ -144,7 +143,11 @@ function createCodeMirrorInsideCell(cellNode, code) { } }); - return editor + return cm +} + +function updateAnyCodeDiffers(hint=false) { + document.body.classList.toggle("code-differs", hint || (document.querySelector("notebook>cell.code-differs") != null)) } function prettytime(time_ns) { @@ -285,6 +288,7 @@ function updateLocalCellInput(byMe, cellNode, code, folded) { cellNode.classList.remove("code-differs") } + updateAnyCodeDiffers() foldLocalCell(cellNode, folded) } @@ -422,31 +426,38 @@ function requestChangeRemoteCell(uuid, createPromise = false) { return client.send("changecell", { code: newCode }, uuid, createPromise) } -function requestRunAllRemoteCells(setInputs = true) { - if (!window.allCellsCompleted) { - return - } +function requestRunAllChangedRemoteCells() { refreshAllCompletionPromise() - const promises = [] - for (var uuid in window.localCells) { - const cellNode = window.localCells[uuid] + const changed = Array.from(notebookNode.querySelectorAll("cell.code-differs")) + const promises = changed.map(cellNode => { + const uuid = cellNode.id cellNode.classList.add("running") - if (setInputs) { - promises.push( - client.sendreceive("setinput", { - code: window.codeMirrors[uuid].getValue() - }, uuid).then(u => { - updateLocalCellInput(true, cellNode, u.message.code, u.message.folded) - }) - ) - } - } + return client.sendreceive("setinput", { + code: window.codeMirrors[uuid].getValue() + }, uuid).then(u => { + updateLocalCellInput(true, cellNode, u.message.code, u.message.folded) + }) + }) Promise.all(promises).then(() => { - client.send("runall", {}) + client.send("runmultiple", { + cells: changed.map(c => c.id) + }) }).catch(console.error) } +function requestRunAllRemoteCells() { + refreshAllCompletionPromise() + + const uuids = Array.from(window.localCells).map(cellNode => { + cellNode.classList.add("running") + return cellNode.id + }) + client.send("runmultiple", { + cells: uuids + }) +} + function requestInterruptRemote() { client.send("interruptall", {}) } @@ -560,7 +571,7 @@ function onEstablishConnection() { if (runAll && !document.querySelector("notebook>cell.running") && document.querySelector("notebook>cell.output-notinsync")) { - requestRunAllRemoteCells(false) + requestRunAllRemoteCells() window.allCellsCompletedPromise.then(happy) } else { // We do a code completion request to trigger starting the workpsace diff --git a/assets/light.css b/assets/light.css index f8f21146d8..3154004186 100644 --- a/assets/light.css +++ b/assets/light.css @@ -160,8 +160,8 @@ preamble>button { preamble>button>span::after { background-size: 17px 17px; display: block; - content: "Run all" !important; - background-image: url(https://cdn.jsdelivr.net/gh/ionic-team/ionicons@5.0.0/src/svg/play-skip-forward-circle-outline.svg); + content: "Submit all changes" !important; + background-image: url(https://cdn.jsdelivr.net/gh/ionic-team/ionicons@5.0.0/src/svg/sync-circle-outline.svg); background-repeat: no-repeat; background-position-x: right; background-position-y: 1px; diff --git a/sample/ui.jl b/sample/ui.jl index 4d9bf3b3c9..8ab1be3e95 100644 --- a/sample/ui.jl +++ b/sample/ui.jl @@ -109,7 +109,6 @@ function onmove(e){ } canvas.onmousedown = e => { - console.log(e) startX = e.layerX startY = e.layerY canvas.onmousemove = onmove diff --git a/src/notebookserver/Notebook.jl b/src/notebookserver/Notebook.jl index 097611c9f7..7b0dddb706 100644 --- a/src/notebookserver/Notebook.jl +++ b/src/notebookserver/Notebook.jl @@ -24,13 +24,8 @@ end Notebook(path::String, cells::Array{Cell,1}) = Notebook(path, cells, uuid1()) Notebook(cells::Array{Cell,1}) = Notebook(tempname() * ".jl", cells) -function selectcell_byuuid(notebook::Notebook, uuid::UUID)::Union{Cell,Nothing} - cellIndex = findfirst(c->c.uuid == uuid, notebook.cells) - if cellIndex === nothing - @warn "Requested non-existing cell with UUID $(uuid)\nTry refreshing the page in your browser." - return nothing - end - notebook.cells[cellIndex] +function cellindex_fromuuid(notebook::Notebook, uuid::UUID)::Union{Int,Nothing} + findfirst(c->c.uuid == uuid, notebook.cells) end # We use a creative delimiter to avoid accidental use in code diff --git a/src/webserver/Dynamic.jl b/src/webserver/Dynamic.jl index 6effaab843..cb416720a0 100644 --- a/src/webserver/Dynamic.jl +++ b/src/webserver/Dynamic.jl @@ -184,8 +184,10 @@ responses[:run] = (body, notebook::Notebook, cell::Cell; initiator::Union{Initia run_reactive_async!(notebook, cell) end -responses[:runall] = (body, notebook::Notebook; initiator::Union{Initiator, Missing}=missing) -> begin - run_reactive_async!(notebook, notebook.cells) +responses[:runmultiple] = (body, notebook::Notebook; initiator::Union{Initiator, Missing}=missing) -> begin + indices = cellindex_fromuuid.([notebook], UUID.(body["cells"])) + cells = [notebook.cells[i] for i in indices if i !== nothing] + run_reactive_async!(notebook, cells) end responses[:getinput] = (body, notebook::Notebook, cell::Cell; initiator::Union{Initiator, Missing}=missing) -> begin diff --git a/src/webserver/WebServer.jl b/src/webserver/WebServer.jl index b37a3dff05..47e8124966 100644 --- a/src/webserver/WebServer.jl +++ b/src/webserver/WebServer.jl @@ -278,14 +278,12 @@ function process_ws_message(parentbody::Dict{String, Any}, clientstream::HTTP.We end if haskey(parentbody, "cellID") - cell = let - cellID = UUID(parentbody["cellID"]) - selectcell_byuuid(notebook, cellID) - end - if cell === nothing + cellID = UUID(parentbody["cellID"]) + index = cellindex_fromuuid(notebook, cellID) + if index === nothing @warn "Remote cell not found locally!" else - push!(args, cell) + push!(args, notebook.cells[index]) end end