Pluto.jl
Pluto.jl copied to clipboard
AbstractPlutoDingetjes.Display.with_js_link to request calculations and data from Julia dynamically
New JavaScript API to request calculations and data from Julia dynamically. This is useful for:
- Lazy loading more data in a display
- An infinite zooming mandelbrot display
- A table with millions of rows, that loads data on demand
- Letting Julia calculate something on-demand, without requiring a
@bindinteraction- A
@bindinput widget that lets you type Julia code with a submit button, but it validates your input while you type. - An interaction where you select two points on a map, and Julia calculates the shortest path between them, and they are displayed on the same map.
- Play chess against an AI written in Julia.
- A
Matching AbstractPlutoDingetjes PR: https://github.com/JuliaPluto/AbstractPlutoDingetjes.jl/pull/13
Supersedes #991 and #2392
API
The easiest way to use this API is with HypertextLiteral. The use is very similar to APD.Display.published_to_js: the new function AbstractPlutoDingetjes.Display.with_js_link returns a "piece of JavaScript code" that you interpolate directly into a <script> tag.
In JavaScript, the "piece of JavaScript code" returns a function. You can call this function with an argument (which will be passed to your Julia function), and it returns a Promise that resolves to the answer from your Julia function.
@htl("""
<script>
const sqrt_from_julia = $(AbstractPlutoDingetjes.Display.with_js_link(sqrt))
// I can now call sqrt_from_julia like a JavaScript function. It returns a Promise:
const result = await sqrt_from_julia(9.0)
console.log(result)
</script>
""")
Serialization and inner workings
The request and response use Pluto's internal communication protocol (with MsgPack and WebSocket), so the performance is optimal. The same serialization is used as published_to_js, so in particular, Vector{Float64} or Vector{UInt8} are really fast.
Since this API is designed for one-off requests, this communication does not go through Pluto's state management (the request and response are not stored in the state). If multiple clients are connected in parallel, then the messages are not shared between clients.
When not to use it
This API is only meant to support use cases that can not be covered with @bind and Display.published_to_js. If possible, the use of these APIs is preferred over with_js_link because they will work with the Static HTML export and PlutoSliderServer.
Example
Code
@htl("""
<div>
<p>Input:<br>
<input> <input type="submit"></p>
<p>Output:<br>
<textarea cols=40 rows=5></textarea>
<script>
let sqrt_with_julia = $(AbstractPlutoDingetjes.Display.with_js_link(sqrt))
let wrapper = currentScript.closest("div")
let input = wrapper.querySelector("input")
let submit = wrapper.querySelector("input[type='submit']")
let output = wrapper.querySelector("textarea")
submit.addEventListener("click", async () => {
let result = await sqrt_with_julia(Number(input.value))
output.innerText = result
})
</script>
</div>
""")
TODO
- [x] Exception handling inside the callback
- [x] Logging inside the callback
- [x] Stdout inside the callback?
- [ ] Running the callback async or in a thread? ~Right now the callback runs
@asyncon the web server.~ Right now it runs sequentially with other code execution. But then lots of calls can clog up the queue... And queued messages can be invalid once they get executed. - [ ] What should happen when the notebook is running while using the callback? What if the cell itself is running?
- [ ] What happens when the callback takes long?
- [ ] Make sure that everything is cleaned up properly
- [ ] Tests
- [ ] Docs
- [ ] Future-proof to allow a bi-directional API in the future. EDIT: with the invalidation callback it can be done! Need to make a MWE and maybe test it
- [x] Could be
published_to_js(::Function)... EDIT: that would be cute but thenis_supported_by_displaycan't check for it soooooo no :( - [x] Add an invallidation callback (I already have this on my computer but still need to push)
- [ ] What about PlutoSliderServer? Maybe also a
pure::Boolkwarg to declare that your callback is pure? Or maybe that should be assumed by default? (Yes because multiple clients can connect to the same notebook) How do you declare the possible values etc?
Try this Pull Request!
Open Julia and type:
julia> import Pkg
julia> Pkg.activate(temp=true)
julia> Pkg.add(url="https://github.com/fonsp/Pluto.jl", rev="AbstractPlutoDingetjes.Display.with_js_link")
julia> using Pluto
How will this help to better support JSServe?
@Pangoraw this API could easily be used with a WASM backend, right? E.g. when you export to HTML, each js link could be compiled to WASM, and the WASM blob is inserted?
Hey @SimonDanisch!
I think the main work for better JSServe support is using the existing AbstractPlutoDingetjes.Display.published_to_js API for the initial state object (e.g. the initial plot data with WebGLMakie), and the JavaScript this and invalidation API for a persistent display that can update instead of just re-render (see the Web notebooks on https://featured.plutojl.org/).
I don't think this PR will help with https://github.com/fonsp/Pluto.jl/issues/2706, but we can use it to make JSServe use Pluto's existing websocket connection instead of a new one. But right now this PR is one-directional (only JS can initiate), not bidirectional.
but we can use it to make JSServe use Pluto's existing websocket connection instead of a new one.
How will JSServe use the websocket connection if it's not bi-directional?
Yeah exactly, it's not usable for this yet.
https://github.com/fonsp/Pluto.jl/assets/6933510/d450fc84-723c-4f06-8364-f932487203ad
@Pangoraw this API could easily be used with a WASM backend, right? E.g. when you export to HTML, each js link could be compiled to WASM, and the WASM blob is inserted?
Yes! Though, it will probably need to provide the signature to know which method to compile:
const sqrt_from_julia = $(AbstractPlutoDingetjes.Display.with_js_link(sqrt, (Float32,)))
So, it's not really superseding those PRs, right? Is there a plan to actually implement those features still?
I think the main work for better JSServe support is using the existing AbstractPlutoDingetjes.Display.published_to_js API
Are there any docs that explain well, what I will need to do?
@SimonDanisch Can we continue this another time? We'll work on better docs 💛
Implementing the explorable mandelbrot set is so much easier with this PR!
https://github.com/fonsp/Pluto.jl/assets/12846528/009a01c1-8ad5-49c5-b7e1-98ff68ac8c5e
This code simply recomputes the mandelbrot set with a fixed number of points per dimension (500 over x and 500 over y) over the current range of the axes of the plot. So it is recomputed upon zoom or upon panning.
The notebook code hidden below should use the version of AbstractPlutoDingetjes from this branch, but it will be overwritten to the latest registered version when adding or removing packages (I believe)
Notebook Code
### A Pluto.jl notebook ###
# v0.19.32
using Markdown
using InteractiveUtils
# ╔═╡ d6a4d6f0-8920-11ee-1360-dbb4ff626eb6
begin
using PlutoPlotly
using HypertextLiteral
using AbstractPlutoDingetjes
end
# ╔═╡ b4655ee1-dfe3-47a4-8b19-48c9fb2b6349
# This code is taken from a Mandelbrot notebook shared in https://discourse.julialang.org/t/mandelbrot-with-plots-jl/52410
begin
to_range(raw_range, n) = range(minimum(raw_range), maximum(raw_range); length=n)
function mandel(z:: Complex)
c = z
maxiter = 128
for n = 1:maxiter
if abs2(z) > 4
return n-1
end
z = z^2 + c
end
return maxiter
end
function mandel_slice!(grid, ix, x, yrange)
@inbounds for (iy, y) in enumerate(yrange)
grid[iy, ix] = mandel(Complex(x,y))
end
end
function mandel_grid(xrange, yrange)
grid = Matrix{UInt8}(undef, length(xrange), length(yrange))
futures = Vector{Task}(undef, length(xrange))
@inbounds for (ix, x) in enumerate(xrange)
futures[ix] = Threads.@spawn mandel_slice!(grid, ix, x, yrange)
end
wait.(futures)
grid
end
end
# ╔═╡ 059bbb92-5a68-46d7-a210-69fc1ce91aff
let # This code generates the plot with re-computation upon zooming
n = 500
x = to_range([-1, 1], n)
y = to_range([-1, 1], n)
z = mandel_grid(x,y)
data = heatmap(;x,y,z)
p = plot(data, Layout(xaxis_range = [-1, 1], yaxis = attr(;
range = [-1,1],
scaleanchor = "x",
scaleratio = 1,
)))
add_plotly_listener!(p, "plotly_relayout", htl_js("async (e) => {
let xrange = PLOT._fullLayout.xaxis.range
let yrange = PLOT._fullLayout.yaxis.range
if (PLOT.updated_data == undefined) {return}
console.log(PLOT.updated_data)
let update = await PLOT.updated_data({x: xrange, y: yrange})
Plotly.restyle(PLOT, update)
}"))
f(d) = let
x = range(d["x"]..., n) |> collect
y = range(d["y"]..., n) |> collect
z = mandel_grid(x,y) |> PlutoPlotly._preprocess
out = (;
x = [x],
y = [y],
z = [z],
)
out
end
@htl("""
$p
<script>
const plt = currentScript.closest('pluto-cell').querySelector('.js-plotly-plot')
plt.updated_data = $(AbstractPlutoDingetjes.Display.with_js_link(f))
</script>
""")
end
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
AbstractPlutoDingetjes = "6e696c72-6542-2067-7265-42206c756150"
HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
PlutoPlotly = "8e989ff0-3d88-8e9f-f020-2b208a939ff0"
[compat]
AbstractPlutoDingetjes = "~1.2.0"
HypertextLiteral = "~0.9.5"
PlutoPlotly = "~0.4.3"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
julia_version = "1.10.0-beta3"
manifest_format = "2.0"
project_hash = "cc56535668b6e8901cb9ab7a99dbc6e210e21f3b"
[[deps.AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "308ee5441d5a481d4ce0152d04f3ef411b05c2b4"
repo-rev = "Display.with_js_link"
repo-url = "https://github.com/JuliaPluto/AbstractPlutoDingetjes.jl.git"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.2.0"
[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
version = "1.1.1"
[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[deps.BaseDirs]]
git-tree-sha1 = "1c9b6f39f40dba0ef22244a175e2d4e42c8f6ee7"
uuid = "18cc8868-cbac-4acf-b575-c8ff214dc66f"
version = "1.2.0"
[[deps.ColorSchemes]]
deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"]
git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.24.0"
[[deps.ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.4"
[[deps.ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"]
git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.10.0"
[deps.ColorVectorSpace.extensions]
SpecialFunctionsExt = "SpecialFunctions"
[deps.ColorVectorSpace.weakdeps]
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
[[deps.Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.10"
[[deps.CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "1.0.5+1"
[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
[[deps.DelimitedFiles]]
deps = ["Mmap"]
git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae"
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
version = "1.9.1"
[[deps.DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.9.3"
[[deps.Downloads]]
deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
version = "1.6.0"
[[deps.FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
[[deps.FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"
[[deps.HypertextLiteral]]
deps = ["Tricks"]
git-tree-sha1 = "7134810b1afce04bbc1045ca1985fbe81ce17653"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.5"
[[deps.InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
[[deps.JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.4"
[[deps.LaTeXStrings]]
git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.3.1"
[[deps.LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
version = "0.6.4"
[[deps.LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
version = "8.0.1+1"
[[deps.LibGit2]]
deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
[[deps.LibGit2_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"]
uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5"
version = "1.6.4+0"
[[deps.LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
version = "1.11.0+1"
[[deps.Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
[[deps.LinearAlgebra]]
deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
[[deps.Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
[[deps.Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
[[deps.MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
version = "2.28.2+1"
[[deps.Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
[[deps.MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
version = "2023.1.10"
[[deps.NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
version = "1.2.0"
[[deps.OpenBLAS_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"
version = "0.3.23+2"
[[deps.OrderedCollections]]
git-tree-sha1 = "2e73fe17cac3c62ad1aebe70d44c963c3cfdc3e3"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.6.2"
[[deps.Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.3"
[[deps.Parsers]]
deps = ["Dates", "PrecompileTools", "UUIDs"]
git-tree-sha1 = "a935806434c9d4c506ba941871b327b96d41f2bf"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.8.0"
[[deps.Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
version = "1.10.0"
[[deps.PlotlyBase]]
deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"]
git-tree-sha1 = "56baf69781fc5e61607c3e46227ab17f7040ffa2"
uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
version = "0.8.19"
[[deps.PlutoPlotly]]
deps = ["AbstractPlutoDingetjes", "BaseDirs", "Colors", "Dates", "Downloads", "HypertextLiteral", "InteractiveUtils", "LaTeXStrings", "Markdown", "Pkg", "PlotlyBase", "Reexport", "TOML"]
git-tree-sha1 = "0b8880a45f96d8404ae1cf6e4d715e3a79369441"
uuid = "8e989ff0-3d88-8e9f-f020-2b208a939ff0"
version = "0.4.3"
[deps.PlutoPlotly.extensions]
PlotlyKaleidoExt = "PlotlyKaleido"
UnitfulExt = "Unitful"
[deps.PlutoPlotly.weakdeps]
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
[[deps.PrecompileTools]]
deps = ["Preferences"]
git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f"
uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
version = "1.2.0"
[[deps.Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.4.1"
[[deps.Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
[[deps.REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
[[deps.Random]]
deps = ["SHA"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
[[deps.Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"
[[deps.Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.3.0"
[[deps.SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
version = "0.7.0"
[[deps.Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
[[deps.Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
[[deps.SparseArrays]]
deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
version = "1.10.0"
[[deps.Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
version = "1.10.0"
[[deps.SuiteSparse_jll]]
deps = ["Artifacts", "Libdl", "Pkg", "libblastrampoline_jll"]
uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c"
version = "7.2.0+1"
[[deps.TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
version = "1.0.3"
[[deps.Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
version = "1.10.0"
[[deps.TensorCore]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6"
uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50"
version = "0.1.1"
[[deps.Tricks]]
git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f"
uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775"
version = "0.1.8"
[[deps.UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
[[deps.UnPack]]
git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b"
uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
version = "1.0.2"
[[deps.Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
[[deps.Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
version = "1.2.13+1"
[[deps.libblastrampoline_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
version = "5.8.0+1"
[[deps.nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
version = "1.52.0+1"
[[deps.p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
version = "17.4.0+2"
"""
# ╔═╡ Cell order:
# ╠═d6a4d6f0-8920-11ee-1360-dbb4ff626eb6
# ╠═b4655ee1-dfe3-47a4-8b19-48c9fb2b6349
# ╠═059bbb92-5a68-46d7-a210-69fc1ce91aff
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002
@disberd thanks for the encouragement! I updated the TODO list, we should be able to merge it soon :)
Any progress with this pull request? I was actually developing a package to visualize the analogues of the Mandelbrot and Julia sets for other families of functions and having an UI integrated with Pluto would be ideal! I'm using GLMakie currently, but I like how responsive @disberd plots were in the video.
Also, what do I need to do to run that notebook? I tried installing both Pluto and AbstractPlutoDingetjes using the branches from this PR, but the plots don't update after the initial plot. The sqrt example also doesn't work (I get an Uncaught (in promise) TypeError: pluto_actions.request_js_link_response(...) is not iterable in the browser console, but no errors in Julia).
I confirm that with the current status of this branch I am not able to reproduce any of the examples currently shown here as I also get a JS error about some object not being iterable.
This simple cell code
@htl("""
<script>
let asd = $(AbstractPlutoDingetjes.Display.with_js_link(x -> x+1))
const lol = await(asd(1))
</script>
""")
produces this error on JS:
@fonsp is there anything we can do to help finalize this PR? I find new cool use cases for this functionality basically every week so it would be very cool to have it available :D
Hey all! Sorry, this feature is taking a lot of work to go from a prototype to a release. Once we release, we ideally don't need to change the API again, so I am trying to cover as many use cases as possible from the start.
Difficult things are:
- Writing tests (in this PR) (see last point)
- Writing docs (in https://github.com/JuliaPluto/AbstractPlutoDingetjes.jl/pull/13 ). If you have some time then your contribution would be welcome! Try to match the style of other APD docs, use
@htland keep the examples simple :) You can make a PR or just comment on the existing PR with the contents of the docstring. - Async/threaded behaviour: should with_js_link calculations be executed async (this mostly works, except when using functions defined in other cells), or sync, using
notebook.execute_token? - We want to future-proof the ability to write a bidirectional link with this API. Currently, it looks like this is possible if we add an
invalidationcallback on the Julia side. We need to have a good working example, and write tests for that example.
I recently made the with_js_link callback run synchronous with execution instead of as a background task. This fixed all edge cases, but now it's no longer possible to make a bidirectional callback out of it (which you need for JSServe).
I'm considering releasing this first API first, maybe as with_js_link_sync, and try to support an async link (maybe with #2780) in the future.
Hmmm never mind I think #2780 is good enough to work, and it's much cooler to have an async API :)
Bidirectional link with multiple clients in https://github.com/fonsp/disorganised-mess/blob/main/APD%20with_js_link%20bidirectional%203.jl using #2780
@disberd and @vambrosi there you go! It's merged but not yet released. Can you test your apps with the latest version to make sure everything works? Also check out the new docs on https://github.com/JuliaPluto/AbstractPlutoDingetjes.jl/pull/13
Amazing @fonsp, I am gonna start trying out many things to see if it breaks :D.
The mandelbrot notebook works with Pluto#main and [email protected]
🎉🎉🎉
Updated Mandelbrot notebook
### A Pluto.jl notebook ###
# v0.19.40
using Markdown
using InteractiveUtils
# ╔═╡ 28a24682-9a36-48d0-88c2-88841854df46
begin
using PlutoPlotly
using HypertextLiteral
using AbstractPlutoDingetjes
end
# ╔═╡ 572b1939-b61a-4d81-8000-10cac4048c8d
# This code is taken from a Mandelbrot notebook shared in https://discourse.julialang.org/t/mandelbrot-with-plots-jl/52410
begin
to_range(raw_range, n) = range(minimum(raw_range), maximum(raw_range); length=n)
function mandel(z:: Complex)
c = z
maxiter = 128
for n = 1:maxiter
if abs2(z) > 4
return n-1
end
z = z^2 + c
end
return maxiter
end
function mandel_slice!(grid, ix, x, yrange)
@inbounds for (iy, y) in enumerate(yrange)
grid[iy, ix] = mandel(Complex(x,y))
end
end
function mandel_grid(xrange, yrange)
grid = Matrix{UInt8}(undef, length(xrange), length(yrange))
futures = Vector{Task}(undef, length(xrange))
@inbounds for (ix, x) in enumerate(xrange)
futures[ix] = Threads.@spawn mandel_slice!(grid, ix, x, yrange)
end
wait.(futures)
grid
end
end
# ╔═╡ da487aae-0670-4199-a8d4-ebea52ab2896
let # This code generates the plot with re-computation upon zooming
n = 500
x = to_range([-1, 1], n)
y = to_range([-1, 1], n)
z = mandel_grid(x,y)
data = heatmap(;x,y,z)
p = plot(data, Layout(xaxis_range = [-1, 1], yaxis = attr(;
range = [-1,1],
scaleanchor = "x",
scaleratio = 1,
)))
add_plotly_listener!(p, "plotly_relayout", htl_js("async (e) => {
let xrange = PLOT._fullLayout.xaxis.range
let yrange = PLOT._fullLayout.yaxis.range
if (PLOT.updated_data == undefined) {return}
let update = await PLOT.updated_data({x: xrange, y: yrange})
Plotly.restyle(PLOT, update)
}"))
f(d) = let
x = range(d["x"]..., n) |> collect
y = range(d["y"]..., n) |> collect
z = mandel_grid(x,y) |> PlutoPlotly._preprocess
out = (;
x = [x],
y = [y],
z = [z],
)
out
end
@htl("""
$p
<script>
const plt = currentScript.closest('pluto-cell').querySelector('.js-plotly-plot')
plt.updated_data = $(AbstractPlutoDingetjes.Display.with_js_link(f))
</script>
""")
end
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
AbstractPlutoDingetjes = "6e696c72-6542-2067-7265-42206c756150"
HypertextLiteral = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
PlutoPlotly = "8e989ff0-3d88-8e9f-f020-2b208a939ff0"
[compat]
AbstractPlutoDingetjes = "~1.3.0"
HypertextLiteral = "~0.9.5"
PlutoPlotly = "~0.4.5"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
julia_version = "1.10.1"
manifest_format = "2.0"
project_hash = "d816ed2045a595d53cb3c87792dc8e3c6d5b003e"
[[deps.AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "0f748c81756f2e5e6854298f11ad8b2dfae6911a"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.3.0"
[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
version = "1.1.1"
[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[deps.BaseDirs]]
git-tree-sha1 = "3e93fcd95fe8db4704e98dbda14453a0bfc6f6c3"
uuid = "18cc8868-cbac-4acf-b575-c8ff214dc66f"
version = "1.2.3"
[[deps.ColorSchemes]]
deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"]
git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.24.0"
[[deps.ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.4"
[[deps.ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"]
git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.10.0"
[deps.ColorVectorSpace.extensions]
SpecialFunctionsExt = "SpecialFunctions"
[deps.ColorVectorSpace.weakdeps]
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
[[deps.Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.10"
[[deps.CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "1.1.0+0"
[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
[[deps.DelimitedFiles]]
deps = ["Mmap"]
git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae"
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"
version = "1.9.1"
[[deps.DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.9.3"
[[deps.Downloads]]
deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
version = "1.6.0"
[[deps.FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
[[deps.FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"
[[deps.HypertextLiteral]]
deps = ["Tricks"]
git-tree-sha1 = "7134810b1afce04bbc1045ca1985fbe81ce17653"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.5"
[[deps.InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
[[deps.JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.4"
[[deps.LaTeXStrings]]
git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.3.1"
[[deps.LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
version = "0.6.4"
[[deps.LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
version = "8.4.0+0"
[[deps.LibGit2]]
deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
[[deps.LibGit2_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"]
uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5"
version = "1.6.4+0"
[[deps.LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
version = "1.11.0+1"
[[deps.Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
[[deps.LinearAlgebra]]
deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
[[deps.Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
[[deps.Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
[[deps.MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
version = "2.28.2+1"
[[deps.Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
[[deps.MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
version = "2023.1.10"
[[deps.NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
version = "1.2.0"
[[deps.OpenBLAS_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"
version = "0.3.23+4"
[[deps.OrderedCollections]]
git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.6.3"
[[deps.Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.3"
[[deps.Parsers]]
deps = ["Dates", "PrecompileTools", "UUIDs"]
git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.8.1"
[[deps.Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
version = "1.10.0"
[[deps.PlotlyBase]]
deps = ["ColorSchemes", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"]
git-tree-sha1 = "56baf69781fc5e61607c3e46227ab17f7040ffa2"
uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5"
version = "0.8.19"
[[deps.PlutoPlotly]]
deps = ["AbstractPlutoDingetjes", "BaseDirs", "Colors", "Dates", "Downloads", "HypertextLiteral", "InteractiveUtils", "LaTeXStrings", "Markdown", "Pkg", "PlotlyBase", "Reexport", "TOML"]
git-tree-sha1 = "fbf637823ec24c5669b1a66f3771c2306f60857c"
uuid = "8e989ff0-3d88-8e9f-f020-2b208a939ff0"
version = "0.4.5"
[deps.PlutoPlotly.extensions]
PlotlyKaleidoExt = "PlotlyKaleido"
UnitfulExt = "Unitful"
[deps.PlutoPlotly.weakdeps]
PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
[[deps.PrecompileTools]]
deps = ["Preferences"]
git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f"
uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
version = "1.2.0"
[[deps.Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.4.1"
[[deps.Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
[[deps.REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
[[deps.Random]]
deps = ["SHA"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
[[deps.Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"
[[deps.Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.3.0"
[[deps.SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
version = "0.7.0"
[[deps.Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
[[deps.Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
[[deps.SparseArrays]]
deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
version = "1.10.0"
[[deps.Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
version = "1.10.0"
[[deps.SuiteSparse_jll]]
deps = ["Artifacts", "Libdl", "libblastrampoline_jll"]
uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c"
version = "7.2.1+1"
[[deps.TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
version = "1.0.3"
[[deps.Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
version = "1.10.0"
[[deps.TensorCore]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6"
uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50"
version = "0.1.1"
[[deps.Tricks]]
git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f"
uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775"
version = "0.1.8"
[[deps.UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
[[deps.UnPack]]
git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b"
uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
version = "1.0.2"
[[deps.Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
[[deps.Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
version = "1.2.13+1"
[[deps.libblastrampoline_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
version = "5.8.0+1"
[[deps.nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
version = "1.52.0+1"
[[deps.p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
version = "17.4.0+2"
"""
# ╔═╡ Cell order:
# ╠═28a24682-9a36-48d0-88c2-88841854df46
# ╠═572b1939-b61a-4d81-8000-10cac4048c8d
# ╠═da487aae-0670-4199-a8d4-ebea52ab2896
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002
Super cool!!
Thanks @fonsp, it looks great!
https://github.com/fonsp/Pluto.jl/assets/42151837/90fbdd18-6751-470e-8779-082532c7a309
I just get this flickering though, not sure if this is because of Plotly (you can see it in the video). It also happens with @disberd updated notebook.
@vambrosi, on my laptop I do not see the same flickering as you (at least on my notebook). There is some kind of misalignment only seldomly when zooming in (twice in this video below despite zooming more than twice):
https://github.com/fonsp/Pluto.jl/assets/12846528/b2b1bb5a-ee98-40a1-b46f-06cf97c0995d
Would be nice if you could debug this before I make the release! To me it looks like a plotly issue
Any update?
Just one thing, I figured out that it happens on Firefox, but not on Safari or Chrome. So I am guessing is a Plotly issue.
In case it is still relevant @fonsp, the flickering seems to be an old Plotly issue: https://github.com/plotly/plotly.js/issues/4377 (still checking to see if it is the exact same problem).
@vambrosi This notebook is super cool! Would you be interested in submitting it to https://github.com/JuliaPluto/featured ? Make a PR and we can discuss more there!