JuliaPluto / PlutoSliderServer.jl

Web server to run just the `@bind` parts of a Pluto.jl notebook
https://computationalthinking.mit.edu/
The Unlicense
135 stars 18 forks source link

How to combine PlutoSliderServer with PlutoHooks? #105

Open schlichtanders opened 1 year ago

schlichtanders commented 1 year ago

One central aspect of PlutoSliderServer is the plutohash, which denotes when a file has changed.

Currently plutohash = base64urlencode ∘ sha256. This does not take into account running PlutoHooks use_state, which may change values without neither the file being changed, nor bonds being changed.

If I understood it right, PlutoHooks @use_state is already the perfect abstraction - the hash just needs to include also all plutohook @use_state current values. (I have no clue how easy this in reality, just conceptually it looks easy).

Do you know a workaround for now?

Concretely it would be great to use PlutoHooks to update things in the background automatically by e.g. file triggers, and still be able to serve the Pluto notebook to many clients where every client can play with their own bonds (what PlutoSliderServer does, but maybe Pluto itself can already somehow do it).

schlichtanders commented 1 year ago

I looked more into the code-base and it seems this actually really tough to do, as the whole PlutoSliderServer assumes that the hash is defined from the file alone (and will only change if the file changed, which triggers a complete shutdown and restart).

Hence it seems, combining PlutoSliderServer with PlutoHooks asks for a whole new mode where somehow the normal Pluto server runs, and the websocket is in place, but nothing can be edited and bond updates are send via the PlutoSliderServer mechanism.

fonsp commented 1 year ago

Thanks for bringing this up! Cool topic

I think ideally, each session on the live website has its own state running on the PlutoSliderServer. So if two people are using the site at the same time, hooks like @use_state keep a state value for each visitor, and which one is used depends on the visitor making the request, sort of similar to the "session" idea in PHP.

You can already sort-of achieve this right now manually (I think I posted this in an issue before but can't find it), but I'm not sure how to link this with PlutoHooks:

@bind user_id CoolWidget()

client_values = Dict()

@bind str TextField()

let
    prev = get(client_values, user_id, "")
    client_values[user_id] = str
    Text("Last value: $(prev). Current value: $(str)")
end

CoolWidget() = @htl """
<script>
this.value = Math.rand()
this.dispatchEvent(new CustomEvent("input"))
</script>
"""

Instead of a Dict, you could use something like https://github.com/JuliaCollections/LRUCache.jl to keep the memory in check. And then it would be great if this can somehow be used for PlutoHooks "state", I'm sure there's a clever solution to be found.

In any case, by doing @bind user_id CoolWidget(), you make the user's session ID one of the bound values. Because user_id will be codependent with other bound variables (str in this example), it will be sent with each request.