From d52cabfb21a47aa53ef91ccc36970b6ad16d6256 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 21 Dec 2023 22:17:07 +0100 Subject: [PATCH] JSServe got renamed to Bonito (#3477) * JSServe got renamed to Bonito * clean up WGLMakie docs * Bonito got tagged * bonito got tagged * fix benchmark against master --- NEWS.md | 6 +- WGLMakie/Project.toml | 4 +- WGLMakie/README.md | 6 +- WGLMakie/src/Camera.js | 2 +- WGLMakie/src/Serialization.js | 2 +- WGLMakie/src/WGLMakie.jl | 12 +-- WGLMakie/src/display.jl | 30 +++--- WGLMakie/src/picking.jl | 18 ++-- WGLMakie/src/precompiles.jl | 6 +- WGLMakie/src/three_plot.jl | 12 +-- WGLMakie/src/wglmakie.bundled.js | 6 +- WGLMakie/src/wglmakie.js | 2 +- WGLMakie/test/Project.toml | 2 +- WGLMakie/test/offline_export.jl | 4 +- WGLMakie/test/runtests.jl | 16 +-- docs/Project.toml | 4 +- docs/explanations/backends/wglmakie.md | 134 +++++++++++-------------- docs/explanations/headless.md | 10 +- metrics/ttfp/benchmark-ttfp.jl | 4 +- 19 files changed, 131 insertions(+), 149 deletions(-) diff --git a/NEWS.md b/NEWS.md index 5ea2290b211..a96ea390971 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ # News -## master +## master + +- Changes for Bonito rename and WGLMakie docs improvements [#3477](https://github.com/MakieOrg/Makie.jl/pull/3477). ## 0.20.3 @@ -160,7 +162,7 @@ - Do less copies of Observables in Attributes + plot pipeline [#2443](https://github.com/MakieOrg/Makie.jl/pull/2443). - Add Search Page and tweak Result Ordering [#2474](https://github.com/MakieOrg/Makie.jl/pull/2474). - Remove all global attributes from TextureAtlas implementation and fix julia#master [#2498](https://github.com/MakieOrg/Makie.jl/pull/2498). -- Use new JSServe, implement WGLMakie picking, improve performance and fix lots of WGLMakie bugs [#2428](https://github.com/MakieOrg/Makie.jl/pull/2428). +- Use new Bonito, implement WGLMakie picking, improve performance and fix lots of WGLMakie bugs [#2428](https://github.com/MakieOrg/Makie.jl/pull/2428). ## v0.19.0 diff --git a/WGLMakie/Project.toml b/WGLMakie/Project.toml index 3bd8df0e7e3..6366436f34b 100644 --- a/WGLMakie/Project.toml +++ b/WGLMakie/Project.toml @@ -9,7 +9,7 @@ FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" FreeTypeAbstraction = "663a7486-cb36-511b-a19d-713bb74d65c9" GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" Hyperscript = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91" -JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9" +Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Observables = "510215fc-4207-5dde-b226-833fc4488ee2" @@ -25,7 +25,7 @@ FileIO = "1.1" FreeTypeAbstraction = "0.10" GeometryBasics = "0.4.1" Hyperscript = "0.0.3, 0.0.4, 0.0.5" -JSServe = "v2.3" +Bonito = "3.0.0" LinearAlgebra = "1.0, 1.6" Makie = "=0.20.3" Observables = "0.5.1" diff --git a/WGLMakie/README.md b/WGLMakie/README.md index a1c44e6efc2..e5b1f6b902f 100644 --- a/WGLMakie/README.md +++ b/WGLMakie/README.md @@ -11,10 +11,10 @@ scatter(rand(4)) In the REPL, this will open a browser tab, that will refresh on a new display. In VSCode, this should open in the plotpane. -You can also embed plots in a JSServe webpage: +You can also embed plots in a Bonito webpage: ```julia -using JSServe +using Bonito app = App() do session, request return DOM.div( DOM.h1("Some Makie Plots:"), @@ -27,7 +27,7 @@ app = App() do session, request ) end isdefined(Main, :server) && close(server) -server = JSServe.Server(app, "127.0.0.1", 8082) +server = Bonito.Server(app, "127.0.0.1", 8082) ``` ## Sponsors diff --git a/WGLMakie/src/Camera.js b/WGLMakie/src/Camera.js index 1a2b02d3655..18f79807ca0 100644 --- a/WGLMakie/src/Camera.js +++ b/WGLMakie/src/Camera.js @@ -65,7 +65,7 @@ export function attach_3d_camera( camera.lookAt(center); const use_orbit_cam = () => - !(JSServe.can_send_to_julia && JSServe.can_send_to_julia()); + !(Bonito.can_send_to_julia && Bonito.can_send_to_julia()); const controls = new OrbitControls(camera, canvas, use_orbit_cam, (e) => in_scene(scene, e) ); diff --git a/WGLMakie/src/Serialization.js b/WGLMakie/src/Serialization.js index 3fc7c66c5a3..992393faf92 100644 --- a/WGLMakie/src/Serialization.js +++ b/WGLMakie/src/Serialization.js @@ -561,7 +561,7 @@ export function deserialize_scene(data, screen) { if (!force) { // we use the threejs orbit controls, if the julia connection is gone // at least for 3d ... 2d is still a todo, and will stay static right now - if (!(JSServe.can_send_to_julia && JSServe.can_send_to_julia())) { + if (!(Bonito.can_send_to_julia && Bonito.can_send_to_julia())) { return; } } diff --git a/WGLMakie/src/WGLMakie.jl b/WGLMakie/src/WGLMakie.jl index 1f132bb4e39..65240bfb962 100644 --- a/WGLMakie/src/WGLMakie.jl +++ b/WGLMakie/src/WGLMakie.jl @@ -1,7 +1,7 @@ module WGLMakie using Hyperscript -using JSServe +using Bonito using Observables using Makie using Colors @@ -11,9 +11,9 @@ using GeometryBasics using PNGFiles using FreeTypeAbstraction -using JSServe: Session -using JSServe: @js_str, onjs, App, ES6Module -using JSServe.DOM +using Bonito: Session +using Bonito: @js_str, onjs, App, ES6Module +using Bonito.DOM using RelocatableFolders: @path @@ -60,8 +60,8 @@ function activate!(; inline::Union{Automatic,Bool}=LAST_INLINE[], screen_config. LAST_INLINE[] = inline Makie.set_active_backend!(WGLMakie) Makie.set_screen_config!(WGLMakie, screen_config) - if !JSServe.has_html_display() - JSServe.browser_display() + if !Bonito.has_html_display() + Bonito.browser_display() end return end diff --git a/WGLMakie/src/display.jl b/WGLMakie/src/display.jl index 583ad525bcf..8c555209965 100644 --- a/WGLMakie/src/display.jl +++ b/WGLMakie/src/display.jl @@ -1,8 +1,8 @@ struct ThreeDisplay - session::JSServe.Session + session::Bonito.Session end -JSServe.session(td::ThreeDisplay) = td.session +Bonito.session(td::ThreeDisplay) = td.session Base.empty!(::ThreeDisplay) = nothing # TODO implement @@ -13,7 +13,7 @@ end function Base.size(screen::ThreeDisplay) # look at d.qs().clientWidth for displayed width js = js"[document.querySelector('canvas').width, document.querySelector('canvas').height]" - width, height = round.(Int, JSServe.evaljs_value(screen.session, js; timeout=100)) + width, height = round.(Int, Bonito.evaljs_value(screen.session, js; timeout=100)) return (width, height) end @@ -32,15 +32,15 @@ function render_with_init(screen, session, scene) return three, canvas, on_init end -function JSServe.jsrender(session::Session, scene::Scene) +function Bonito.jsrender(session::Session, scene::Scene) screen = Screen(scene) three, canvas, on_init = render_with_init(screen, session, scene) return canvas end -function JSServe.jsrender(session::Session, fig::Makie.FigureLike) +function Bonito.jsrender(session::Session, fig::Makie.FigureLike) Makie.update_state_before_display!(fig) - return JSServe.jsrender(session, Makie.get_scene(fig)) + return Bonito.jsrender(session, Makie.get_scene(fig)) end """ @@ -84,7 +84,7 @@ end """ WithConfig(fig::Makie.FigureLike; screen_config...) -Allows to pass a screenconfig to a figure, inside a JSServe.App. +Allows to pass a screenconfig to a figure, inside a Bonito.App. This circumvents using `WGLMakie.activate!(; screen_config...)` inside an App, which modifies these values globally. Example: @@ -107,7 +107,7 @@ function WithConfig(fig::Makie.FigureLike; kw...) return WithConfig(fig, config) end -function JSServe.jsrender(session::Session, wconfig::WithConfig) +function Bonito.jsrender(session::Session, wconfig::WithConfig) fig = wconfig.fig Makie.update_state_before_display!(fig) scene = Makie.get_scene(fig) @@ -211,7 +211,7 @@ function get_three(screen::Screen; timeout = 100, error::Union{Nothing, String}= if isnothing(screen.session) throw_error("Screen has no session. Not yet displayed?"); return nothing end - if !(screen.session.status in (JSServe.RENDERED, JSServe.DISPLAYED, JSServe.OPEN)) + if !(screen.session.status in (Bonito.RENDERED, Bonito.DISPLAYED, Bonito.OPEN)) throw_error("Screen Session uninitialized. Not yet displayed? Session status: $(screen.session.status)"); return nothing end tstart = time() @@ -289,9 +289,9 @@ function session2image(session::Session, scene::Scene) }) }() """ - picture_base64 = JSServe.evaljs_value(session, to_data; timeout=100) + picture_base64 = Bonito.evaljs_value(session, to_data; timeout=100) picture_base64 = replace(picture_base64, "data:image/png;base64," => "") - bytes = JSServe.Base64.base64decode(picture_base64) + bytes = Bonito.Base64.base64decode(picture_base64) return PNGFiles.load(IOBuffer(bytes)) end @@ -334,13 +334,13 @@ end function insert_plot!(disp::ThreeDisplay, scene::Scene, @nospecialize(plot::Plot)) plot_data = serialize_plots(scene, [plot]) plot_sub = Session(disp.session) - JSServe.init_session(plot_sub) + Bonito.init_session(plot_sub) plot.__wgl_session = plot_sub js = js""" $(WGL).then(WGL=> { WGL.insert_plot($(js_uuid(scene)), $plot_data); })""" - JSServe.evaljs_value(plot_sub, js; timeout=50) + Bonito.evaljs_value(plot_sub, js; timeout=50) return end @@ -370,7 +370,7 @@ function delete_js_objects!(screen::Screen, plot_uuids::Vector{String}, three = get_three(screen) isnothing(three) && return # if no session we haven't displayed and dont need to delete isready(three.session) || return - JSServe.evaljs(three.session, js""" + Bonito.evaljs(three.session, js""" $(WGL).then(WGL=> { WGL.delete_plots($(plot_uuids)); })""") @@ -398,7 +398,7 @@ function delete_js_objects!(screen::Screen, scene::Scene) close(session) end end - JSServe.evaljs(three.session, js""" + Bonito.evaljs(three.session, js""" $(WGL).then(WGL=> { WGL.delete_scenes($scene_uuids, $(js_uuid.(plots))); })""") diff --git a/WGLMakie/src/picking.jl b/WGLMakie/src/picking.jl index 1bbf590fdf2..15ed8f4606c 100644 --- a/WGLMakie/src/picking.jl +++ b/WGLMakie/src/picking.jl @@ -4,7 +4,7 @@ function pick_native(screen::Screen, rect::Rect2i) (w, h) = widths(rect) session = get_three(screen; error="Can't do picking!").session scene = screen.scene - picking_data = JSServe.evaljs_value(session, js""" + picking_data = Bonito.evaljs_value(session, js""" Promise.all([$(WGL), $(scene)]).then(([WGL, scene]) => WGL.pick_native_matrix(scene, $x, $y, $w, $h)) """) empty = Matrix{Tuple{Union{Nothing, AbstractPlot}, Int}}(undef, 0, 0) @@ -37,7 +37,7 @@ function Makie.pick_closest(scene::Scene, screen::Screen, xy, range::Integer) xy_vec = Cint[round.(Cint, xy)...] range = round(Int, range) session = get_three(screen; error="Can't do picking!").session - selection = JSServe.evaljs_value(session, js""" + selection = Bonito.evaljs_value(session, js""" Promise.all([$(WGL), $(scene)]).then(([WGL, scene]) => WGL.pick_closest(scene, $(xy_vec), $(range))) """) lookup = plot_lookup(scene) @@ -50,7 +50,7 @@ function Makie.pick_sorted(scene::Scene, screen::Screen, xy, range) range = round(Int, range) session = get_three(screen; error="Can't do picking!").session - selection = JSServe.evaljs_value(session, js""" + selection = Bonito.evaljs_value(session, js""" Promise.all([$(WGL), $(scene)]).then(([WGL, scene]) => WGL.pick_sorted(scene, $(xy_vec), $(range))) """) isnothing(selection) && return Tuple{Union{Nothing,AbstractPlot},Int}[] @@ -69,7 +69,7 @@ end """ ToolTip(figurelike, js_callback; plots=plots_you_want_to_hover) -Returns a JSServe DOM element, which creates a popup whenever you click on a plot element in `plots`. +Returns a Bonito DOM element, which creates a popup whenever you click on a plot element in `plots`. The content of the popup is filled with the return value of js_callback, which can be a string or `HTMLNode`. Usage example: @@ -101,7 +101,7 @@ end """ struct ToolTip scene::Scene - callback::JSServe.JSCode + callback::Bonito.JSCode plot_uuids::Vector{String} function ToolTip(figlike, callback; plots=nothing) scene = Makie.get_scene(figlike) @@ -113,17 +113,17 @@ struct ToolTip end end -const POPUP_CSS = JSServe.Asset(joinpath(@__DIR__, "popup.css")) +const POPUP_CSS = Bonito.Asset(joinpath(@__DIR__, "popup.css")) -function JSServe.jsrender(session::Session, tt::ToolTip) +function Bonito.jsrender(session::Session, tt::ToolTip) scene = tt.scene popup = DOM.div("", class="popup") - JSServe.evaljs(session, js""" + Bonito.evaljs(session, js""" $(scene).then(scene => { const plots_to_pick = new Set($(tt.plot_uuids)); const callback = $(tt.callback); WGL.register_popup($popup, scene, plots_to_pick, callback) }) """) - return DOM.span(JSServe.jsrender(session, POPUP_CSS), popup) + return DOM.span(Bonito.jsrender(session, POPUP_CSS), popup) end diff --git a/WGLMakie/src/precompiles.jl b/WGLMakie/src/precompiles.jl index 0822ed9f3b8..b6be76a9107 100644 --- a/WGLMakie/src/precompiles.jl +++ b/WGLMakie/src/precompiles.jl @@ -9,11 +9,11 @@ macro compile(block) # while precompiling # So we just do all parts of the stack we can do without browser scene = Makie.get_scene(figlike) - session = Session(JSServe.NoConnection(); asset_server=JSServe.NoServer()) + session = Session(Bonito.NoConnection(); asset_server=Bonito.NoServer()) three_display(Screen(scene), session, scene) - JSServe.jsrender(session, figlike) + Bonito.jsrender(session, figlike) s = serialize_scene(scene) - JSServe.SerializedMessage(session, Dict(:data => s)) + Bonito.SerializedMessage(session, Dict(:data => s)) close(session) return nothing end diff --git a/WGLMakie/src/three_plot.jl b/WGLMakie/src/three_plot.jl index 539ff7109bb..581a72ed1a4 100644 --- a/WGLMakie/src/three_plot.jl +++ b/WGLMakie/src/three_plot.jl @@ -3,12 +3,12 @@ # We use objectid to find objects on the js side js_uuid(object) = string(objectid(object)) -function JSServe.print_js_code(io::IO, plot::AbstractPlot, context::JSServe.JSSourceContext) +function Bonito.print_js_code(io::IO, plot::AbstractPlot, context::Bonito.JSSourceContext) uuids = js_uuid.(Makie.collect_atomic_plots(plot)) # This is a bit more complicated then it has to be, since evaljs / on_document_load # isn't guaranteed to run after plot initialization in an App... So, if we don't find any plots, # we have to check again after inserting new plots - JSServe.print_js_code(io, js"""(new Promise(resolve => { + Bonito.print_js_code(io, js"""(new Promise(resolve => { $(WGL).then(WGL=> { const find = ()=> { const plots = WGL.find_plots($(uuids)) @@ -23,8 +23,8 @@ function JSServe.print_js_code(io::IO, plot::AbstractPlot, context::JSServe.JSSo }))""", context) end -function JSServe.print_js_code(io::IO, scene::Scene, context::JSServe.JSSourceContext) - JSServe.print_js_code(io, js"""$(WGL).then(WGL=> WGL.find_scene($(js_uuid(scene))))""", context) +function Bonito.print_js_code(io::IO, scene::Scene, context::Bonito.JSSourceContext) + Bonito.print_js_code(io, js"""$(WGL).then(WGL=> WGL.find_scene($(js_uuid(scene))))""", context) end function three_display(screen::Screen, session::Session, scene::Scene) @@ -38,7 +38,7 @@ function three_display(screen::Screen, session::Session, scene::Scene) comm = Observable(Dict{String,Any}()) done_init = Observable(false) # Keep texture atlas in parent session, so we don't need to send it over and over again - ta = JSServe.Retain(TEXTURE_ATLAS) + ta = Bonito.Retain(TEXTURE_ATLAS) evaljs(session, js""" $(WGL).then(WGL => { try { @@ -53,7 +53,7 @@ function three_display(screen::Screen, session::Session, scene::Scene) } $(done_init).notify(true) } catch (e) { - JSServe.Connection.send_error("error initializing scene", e) + Bonito.Connection.send_error("error initializing scene", e) $(done_init).notify(false) return } diff --git a/WGLMakie/src/wglmakie.bundled.js b/WGLMakie/src/wglmakie.bundled.js index cd50663ea6c..924b0533e0d 100644 --- a/WGLMakie/src/wglmakie.bundled.js +++ b/WGLMakie/src/wglmakie.bundled.js @@ -21016,7 +21016,7 @@ function attach_3d_camera(canvas, makie_camera, cam3d, light_dir, scene) { camera.up = new A(...cam3d.upvector.value); camera.position.set(...cam3d.eyeposition.value); camera.lookAt(center); - const use_orbit_cam = ()=>!(JSServe.can_send_to_julia && JSServe.can_send_to_julia()); + const use_orbit_cam = ()=>!(Bonito.can_send_to_julia && Bonito.can_send_to_julia()); const controls = new OrbitControls(camera, canvas, use_orbit_cam, (e)=>in_scene(scene, e)); controls.addEventListener("change", (e)=>{ const view = camera.matrixWorldInverse; @@ -21883,7 +21883,7 @@ function deserialize_scene(data, screen) { scene.wgl_camera = camera; function update_cam(camera_matrices, force) { if (!force) { - if (!(JSServe.can_send_to_julia && JSServe.can_send_to_julia())) { + if (!(Bonito.can_send_to_julia && Bonito.can_send_to_julia())) { return; } } @@ -22054,7 +22054,7 @@ function on_shader_error(gl, program, glVertexShader, glFragmentShader) { const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); const err = "THREE.WebGLProgram: Shader Error " + wglerror(gl, gl.getError()) + " - " + "VALIDATE_STATUS " + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + "\n\n" + "Program Info Log:\n" + programLog + "\n" + vertexErrors + "\n" + fragmentErrors + "\n" + "Fragment log:\n" + fragmentLog + "Vertex log:\n" + vertexLog; - JSServe.Connection.send_warning(err); + Bonito.Connection.send_warning(err); } function add_canvas_events(screen, comm, resize_to) { const { canvas , winscale } = screen; diff --git a/WGLMakie/src/wglmakie.js b/WGLMakie/src/wglmakie.js index a9986046845..548e04eee79 100644 --- a/WGLMakie/src/wglmakie.js +++ b/WGLMakie/src/wglmakie.js @@ -220,7 +220,7 @@ function on_shader_error(gl, program, glVertexShader, glFragmentShader) { "Vertex log:\n" + vertexLog; - JSServe.Connection.send_warning(err); + Bonito.Connection.send_warning(err); } function add_canvas_events(screen, comm, resize_to) { diff --git a/WGLMakie/test/Project.toml b/WGLMakie/test/Project.toml index 066e2ecdc44..ed6acdb3588 100644 --- a/WGLMakie/test/Project.toml +++ b/WGLMakie/test/Project.toml @@ -1,7 +1,7 @@ [deps] Electron = "a1bb12fb-d4d1-54b4-b10a-ee7951ef7ad3" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9" +Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" ReferenceTests = "d37af2e0-5618-4e00-9939-d430db56ee94" diff --git a/WGLMakie/test/offline_export.jl b/WGLMakie/test/offline_export.jl index 0d08ca7c066..109dca0443e 100644 --- a/WGLMakie/test/offline_export.jl +++ b/WGLMakie/test/offline_export.jl @@ -1,4 +1,4 @@ -using JSServe, WGLMakie, Makie +using Bonito, WGLMakie, Makie function handler(session, request) return scatter(1:4, color=1:4) @@ -6,7 +6,7 @@ end dir = joinpath(@__DIR__, "exported") isdir(dir) || mkdir(dir) -JSServe.export_standalone(handler, dir) +Bonito.export_standalone(handler, dir) # Then serve it with e.g. LiveServer using LiveServer diff --git a/WGLMakie/test/runtests.jl b/WGLMakie/test/runtests.jl index 22135765afd..200d61142c4 100644 --- a/WGLMakie/test/runtests.jl +++ b/WGLMakie/test/runtests.jl @@ -1,6 +1,6 @@ using FileIO using WGLMakie, Makie, Test -using WGLMakie.JSServe +using WGLMakie.Bonito using ReferenceTests import Electron @@ -24,7 +24,7 @@ excludes = Set([ "image scatter", "Line GIF", "surface + contour3d", - # Hm weird, looks like some internal JSServe error missing an Observable: + # Hm weird, looks like some internal Bonito error missing an Observable: "Errorbars x y low high", "Rangebars x y low high", # These are a bit sad, since it's just missing interpolations @@ -51,7 +51,7 @@ excludes = Set([ ]) Makie.inline!(Makie.automatic) -edisplay = JSServe.use_electron_display(devtools=true) +edisplay = Bonito.use_electron_display(devtools=true) @testset "refimages" begin WGLMakie.activate!() ReferenceTests.mark_broken_tests(excludes) @@ -66,7 +66,7 @@ end display(edisplay, app) GC.gc(true); # Somehow this may take a while to get emptied completely - JSServe.wait_for(() -> (GC.gc(true);isempty(run(edisplay.window, "Object.keys(WGL.plot_cache)")));timeout=20) + Bonito.wait_for(() -> (GC.gc(true);isempty(run(edisplay.window, "Object.keys(WGL.plot_cache)")));timeout=20) wgl_plots = run(edisplay.window, "Object.keys(WGL.scene_cache)") @test isempty(wgl_plots) @@ -76,10 +76,10 @@ end @show session_size texture_atlas_size @test session_size / 10^6 < 6 @test texture_atlas_size < 6 - s_keys = "Object.keys(JSServe.Sessions.SESSIONS)" - JSServe.wait_for(() -> (GC.gc(true); 2 == length(run(edisplay.window, s_keys))); timeout=30) - js_sessions = run(edisplay.window, "JSServe.Sessions.SESSIONS") - js_objects = run(edisplay.window, "JSServe.Sessions.GLOBAL_OBJECT_CACHE") + s_keys = "Object.keys(Bonito.Sessions.SESSIONS)" + Bonito.wait_for(() -> (GC.gc(true); 2 == length(run(edisplay.window, s_keys))); timeout=30) + js_sessions = run(edisplay.window, "Bonito.Sessions.SESSIONS") + js_objects = run(edisplay.window, "Bonito.Sessions.GLOBAL_OBJECT_CACHE") # @test Set([app.session[].id, app.session[].parent.id]) == keys(js_sessions) # we used Retain for global_obs, so it should stay as long as root session is open @test keys(js_objects) == Set([WGLMakie.TEXTURE_ATLAS.id]) diff --git a/docs/Project.toml b/docs/Project.toml index 0398c2f82ac..90422c54827 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,7 @@ [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" Animations = "27a7e980-b3e6-11e9-2bcd-0b925532e340" +Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Chain = "8be319e6-bccf-4806-a6f7-6fae938471bc" ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" @@ -16,7 +17,6 @@ GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" Gumbo = "708ec375-b3d6-5a57-a7ce-8257bf98657a" ImageTransformations = "02fcd773-0e25-5acc-982a-7f6622650795" -JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b" MappedArrays = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" @@ -29,4 +29,4 @@ RadeonProRender = "27029320-176d-4a42-b57d-56729d2ad457" WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" [compat] -Documenter = "<1" \ No newline at end of file +Documenter = "<1" diff --git a/docs/explanations/backends/wglmakie.md b/docs/explanations/backends/wglmakie.md index ad521bea170..777115eced3 100644 --- a/docs/explanations/backends/wglmakie.md +++ b/docs/explanations/backends/wglmakie.md @@ -1,7 +1,7 @@ # WGLMakie [WGLMakie](https://github.com/MakieOrg/Makie.jl/tree/master/WGLMakie) is the web-based backend, which is mostly implemented in Julia right now. -WGLMakie uses [JSServe](https://github.com/SimonDanisch/JSServe.jl) to generate the HTML and JavaScript for displaying the plots. On the JavaScript side, we use [ThreeJS](https://threejs.org/) and [WebGL](https://de.wikipedia.org/wiki/WebGL) to render the plots. +WGLMakie uses [Bonito](https://github.com/SimonDanisch/Bonito.jl) to generate the HTML and JavaScript for displaying the plots. On the JavaScript side, we use [ThreeJS](https://threejs.org/) and [WebGL](https://de.wikipedia.org/wiki/WebGL) to render the plots. Moving more of the implementation to JavaScript is currently the goal and will give us a better JavaScript API, and more interaction without a running Julia server. @@ -21,27 +21,27 @@ Moving more of the implementation to JavaScript is currently the goal and will g ##### IJulia -* JSServe now uses the IJulia connection, and therefore can be used even with complex proxy setup without any additional setup +* Bonito now uses the IJulia connection, and therefore can be used even with complex proxy setup without any additional setup * reload of the page isn't supported, if you reload, you need to re-execute all cells and make sure that `Page()` is executed first. #### JupyterHub / Jupyterlab / Binder -* WGLMakie should mostly work with a websocket connection. JSServe tries to [infer the proxy setup](https://github.com/SimonDanisch/JSServe.jl/blob/master/src/server-defaults.jl) needed to connect to the julia process. On local jupyterlab instances, this should work without problem, on hosted ones one may need add `jupyter-server-proxy`. See: +* WGLMakie should mostly work with a websocket connection. Bonito tries to [infer the proxy setup](https://github.com/SimonDanisch/Bonito.jl/blob/master/src/server-defaults.jl) needed to connect to the julia process. On local jupyterlab instances, this should work without problem, on hosted ones one may need add `jupyter-server-proxy`. See: * https://github.com/MakieOrg/Makie.jl/issues/2464 * https://github.com/MakieOrg/Makie.jl/issues/2405 #### Pluto -* still uses JSServe's Websocket connection, so needs extra setup for remote servers. +* still uses Bonito's Websocket connection, so needs extra setup for remote servers. * reload of the page isn't supported, if you reload, you need to re-execute all cells and make sure that `Page()` is executed first. * static html export not fully working yet #### JuliaHub * VSCode in the browser should work out of the box. -* Pluto in JuliaHub still has a [problem](https://github.com/SimonDanisch/JSServe.jl/issues/140) with the websocket connection. So, you will see a plot, but interaction doesn't work. +* Pluto in JuliaHub still has a [problem](https://github.com/SimonDanisch/Bonito.jl/issues/140) with the WebSocket connection. So, you will see a plot, but interaction doesn't work. #### Browser Support @@ -66,13 +66,13 @@ println("~~~") ## Output -You can use JSServe and WGLMakie in Pluto, IJulia, Webpages and Documenter to create interactive apps and dashboards, serve them on live webpages, or export them to static HTML. +You can use Bonito and WGLMakie in Pluto, IJulia, Webpages and Documenter to create interactive apps and dashboards, serve them on live webpages, or export them to static HTML. This tutorial will run through the different modes and what kind of limitations to expect. ### Page -`Page()` can be used to reset the JSServe state needed for multipage output like it's the case for `Documenter` or the various notebooks (IJulia/Pluto/etc). +`Page()` can be used to reset the Bonito state needed for multipage output like it's the case for `Documenter` or the various notebooks (IJulia/Pluto/etc). Previously, it was necessary to always insert and display the `Page` call in notebooks, but now the call to `Page()` is optional and doesn't need to be displayed. What it does is purely reset the state for a new multi-page output, which is usually the case for `Documenter`, which creates multiple pages in one Julia session, or you can use it to reset the state in notebooks, e.g. after a page reload. `Page(exportable=true, offline=true)` can be used to force inlining all data & js dependencies, so that everything can be loaded in a single HTML object without a running Julia process. The defaults should already be chosen this way for e.g. Documenter, so this should mostly be used for e.g. `Pluto` offline export (which is currently not fully supported, but should be soon). @@ -82,7 +82,7 @@ Here is an example of how to use this in Franklin: \begin{showhtml}{} ```julia using WGLMakie -using JSServe, Markdown +using Bonito, Markdown Page(exportable=true, offline=true) # for Franklin, you still need to configure WGLMakie.activate!() Makie.inline!(true) # Make sure to inline plots into Documenter output! @@ -115,7 +115,7 @@ There are a couple of ways to keep interacting with Plots in a static export. ## Record a statemap -JSServe allows to record a statemap for all widgets, that satisfy the following interface: +Bonito allows to record a statemap for all widgets, that satisfy the following interface: ```julia # must be true to be found inside the DOM @@ -149,19 +149,19 @@ App() do session::Session end heatmap(fig[1, 2], slice) slider = DOM.div("z-index: ", index_slider, index_slider.value) - return JSServe.record_states(session, DOM.div(slider, fig)) + return Bonito.record_states(session, DOM.div(slider, fig)) end ``` \end{showhtml} ## Execute Javascript directly -JSServe makes it easy to build whole HTML and JS applications. -You can for example directly register javascript function that get run on change. +Bonito makes it easy to build whole HTML and JS applications. +You can for example directly register JavaScript function that get run on change. \begin{showhtml}{} ```julia -using JSServe +using Bonito App() do session::Session s1 = Slider(1:100) @@ -188,7 +188,7 @@ But while this isn't in place, logging the the returned object makes it pretty e \begin{showhtml}{} ```julia -using JSServe: on_document_load +using Bonito: on_document_load using WGLMakie App() do session::Session @@ -261,7 +261,7 @@ This summarizes the current state of interactivity with WGLMakie inside static p `Makie.DataInspector` works just fine with WGLMakie, but it requires a running Julia process to show and update the tooltip. There is also a way to show a tooltip in Javascript directly, which needs to be inserted into the HTML dom. -This means, we actually need to use `JSServe.App` to return a `DOM` object: +This means, we actually need to use `Bonito.App` to return a `DOM` object: \begin{showhtml}{} ```julia @@ -299,55 +299,42 @@ Locally, WGLMakie should just work out of the box for Pluto/IJulia, but if you'r ```julia begin - using JSServe + using Bonito some_forwarded_port = 8080 Page(listen_url="0.0.0.0", listen_port=some_forwarded_port) end ``` Or also specify a proxy URL, if you have a more complex proxy setup. -For more advanced setups consult the `?Page` docs and `JSServe.configure_server!`. -In the [headless](/explanations/headless/index.html#wglmakie) documentation, you can also read more about setting up the JSServe server and port forwarding. +For more advanced setups consult the `?Page` docs and `Bonito.configure_server!`. +In the [headless](/explanations/headless/index.html#wglmakie) documentation, you can also read more about setting up the Bonito server and port forwarding. ## Styling -You may have noticed, styling isn't really amazing right now. -The good news is, that one can use the whole mighty power of the CSS/HTML universe. -If it wasn't clear so far, JSServe allows to load arbitrary css, and `DOM.xxx` wraps all existing HTML tags. - -Tailwind is quite a amazing and has a great documentation especially for CSS beginners: -https://tailwindcss.com/docs/ - -Note, that JSServe.TailwindCSS is nothing but: - -```julia -TailwindCSS = JSServe.Asset("/path/to/tailwind.min.css") -``` - -So any other CSS file can be used. - -It's also pretty easy to make reusable blocks from styled elements. -E.g. the `rows` function above is nothing but: +Bonito allows to load arbitrary css, and `DOM.xxx` wraps all existing HTML tags. +So any CSS file can be used, e.g. even libraries like [Tailwind](https://tailwindcss.com/) with `Asset`: ```julia -rows(args...; class="") = DOM.div(args..., class=class * " flex flex-row") +TailwindCSS = Bonito.Asset("/path/to/tailwind.min.css") ``` -It would be more correct to define it as: +Bonito also offers the `Styles` type, which allows to define whole stylesheets and assign them to any DOM object. +That's how Bonito creates styleable components: ```julia -rows(args...; class="") = DOM.div(JSServe.TailwindCSS, args..., class=class * " flex flex-row") +Rows(args...) = DOM.div(args..., style=Styles( + "display" => "grid", + "grid-template-rows" => "fr", + "grid-template-columns" => "repeat($(length(args)), fr)", +)) ``` +This Style object will only be inserted one time into the DOM in one Session, and subsequent uses will just give the div the same class. -JSServe will then make sure, that `JSServe.TailwindCSS` is loaded, and will only load it once! - - -Note, that JSServe.TailwindDashboard already defines something like the above `rows`: +Note, that Bonito already defines something like the above `Rows`: \begin{showhtml}{} ```julia using Colors -using JSServe -import JSServe.TailwindDashboard as D +using Bonito App() do session::Session hue_slider = Slider(0:360) @@ -355,63 +342,45 @@ App() do session::Session onjs(session, hue_slider.value, js"""function (hue){ $(color_swatch).style.backgroundColor = "hsl(" + hue + ",60%,50%)" }""") - return D.FlexRow(hue_slider, color_swatch) + return Row(hue_slider, color_swatch) end ``` \end{showhtml} -With this, we can create a styled, reusable card componenent: +Bonito also offers a styleable Card component: \begin{showhtml}{} ```julia using Markdown -struct GridCard - elements::Any -end - -GridCard(elements...) = GridCard(elements) - -function JSServe.jsrender(card::GridCard) - return DOM.div(JSServe.TailwindCSS, card.elements..., class="rounded-lg p-2 m-2 shadow-lg grid auto-cols-max grid-cols-2 gap-4") -end - App() do session::Session # We can now use this wherever we want: - fig = Figure(size=(200, 200)) + fig = Figure(size=(300, 300)) contour(fig[1,1], rand(4,4)) - card = GridCard( - Slider(1:100), - DOM.h1("hello"), + card = Card(Grid( + Centered(DOM.h1("Hello"); style=Styles("grid-column" => "1 / 3")), + StylableSlider(1:100; style=Styles("grid-column" => "1 / 3")), DOM.img(src="https://julialang.org/assets/infra/logo.svg"), - fig - ) + fig; columns="1fr 1fr", justify_items="stretch" + )) # Markdown creates a DOM as well, and you can interpolate # arbitrary jsrender'able elements in there: - return md""" - - # Wow, Markdown works as well? - - $(card) - - """ + return DOM.div(card) end ``` \end{showhtml} -Hopefully, over time there will be helper libraries with lots of stylised elements like the above, to make flashy dashboards with JSServe + WGLMakie. +Hopefully, over time there will be helper libraries with lots of stylised elements like the above, to make flashy dashboards with Bonito + WGLMakie. # Export Documenter just renders the plots + Page as html, -so if you want to inline WGLMakie/JSServe objects into your own page, +so if you want to inline WGLMakie/Bonito objects into your own page, one can just use something like this: ```julia -using WGLMakie, JSServe - -using WGLMakie, JSServe +using WGLMakie, Bonito, FileIO WGLMakie.activate!() open("index.html", "w") do io @@ -423,10 +392,19 @@ open("index.html", "w") do io """) Page(exportable=true, offline=true) # Then, you can just inline plots or whatever you want :) - show(io, MIME"text/html"(), scatter(1:4)) - show(io, MIME"text/html"(), surface(rand(4, 4))) - # or anything else from JSServe, or that can be displayed as html: - show(io, MIME"text/html"(), JSServe.Slider(1:3)) + # Of course it would make more sense to put this into a single app + app = App() do + C(x;kw...) = Card(x; height="fit-content", width="fit-content", kw...) + figure = (; size=(300, 300)) + f1 = scatter(1:4; figure) + f2 = mesh(load(assetpath("brain.stl")); figure) + C(DOM.div( + Bonito.StylableSlider(1:100), + Row(C(f1), C(f2)) + ); padding="30px", margin="15px") + end + show(io, MIME"text/html"(), app) + # or anything else from Bonito, or that can be displayed as html: println(io, """ diff --git a/docs/explanations/headless.md b/docs/explanations/headless.md index d587ecd4e45..17237a25a7f 100644 --- a/docs/explanations/headless.md +++ b/docs/explanations/headless.md @@ -46,7 +46,7 @@ This procedure is used in the [GLMakie tests](https://github.com/MakieOrg/Makie. ## WGLMakie -For WGLMakie, you can setup a server with JSServe and serve the content from a remote server. +For WGLMakie, you can setup a server with Bonito and serve the content from a remote server. This also works for creating interactive plots with Documenter. Check out the [docs](/explanations/backends/wglmakie) for more details about this. @@ -56,16 +56,16 @@ If you don't need to change the port, you will just have to [forward](https://co If you want to change the port on which WGLMakie runs on the remote, say `8081`, you will have to use the following ```julia -using JSServe +using Bonito -JSServe.configure_server!(listen_port=8081) +Bonito.configure_server!(listen_port=8081) ``` before any plotting commands with WGLMakie. If you also need to use a different port than `8081` on the _local_ machine, say `8080`, you will also need to set the `forwarded_port` like this: ```julia -using JSServe +using Bonito -JSServe.configure_server!(listen_port=8081, forwarded_port=8080) +Bonito.configure_server!(listen_port=8081, forwarded_port=8080) ``` diff --git a/metrics/ttfp/benchmark-ttfp.jl b/metrics/ttfp/benchmark-ttfp.jl index 74a76215495..274b1c3e38d 100644 --- a/metrics/ttfp/benchmark-ttfp.jl +++ b/metrics/ttfp/benchmark-ttfp.jl @@ -10,7 +10,9 @@ t_using = @ctime @eval using $Package if Package === :WGLMakie import Electron - WGLMakie.JSServe.use_electron_display() + # Backwards compatibility for master + Bonito = isdefined(WGLMakie, :Bonito) ? WGLMakie.Bonito : WGLMakie.JSServe + Bonito.use_electron_display() end set_theme!(size=(800, 600))