From 70be0f6ab287861b1283d0d0c5b171f25d4b5f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Sun, 16 Feb 2025 23:21:35 +0100 Subject: [PATCH] add compat routes for GenieBuilder --- src/Elements.jl | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ src/Stipple.jl | 23 +++++++++++++ 2 files changed, 112 insertions(+) diff --git a/src/Elements.jl b/src/Elements.jl index 5f88ca6..a420317 100644 --- a/src/Elements.jl +++ b/src/Elements.jl @@ -251,6 +251,95 @@ function vue_integration(::Type{M}; output[2:prevind(output, lastindex(output))] end +function vue2_integration(::Type{M}; + vue_app_name::String = "StippleApp", + core_theme::Bool = true, + debounce::Int = Stipple.JS_DEBOUNCE_TIME, + transport::Module = Genie.WebChannels)::String where {M<:ReactiveModel} + model = Base.invokelatest(M) + + vue_app = replace(json(model |> Stipple.render), "\"{" => " {", ", filterMixin" => "") + vue_app = replace(vue_app, "}\"" => "} ") + vue_app = replace(vue_app, "\"$(getchannel(model))\"" => Stipple.channel_js_name) + + output = + string( + " + + function initStipple(rootSelector){ + Stipple.init($( core_theme ? "{theme: '$theme'}" : "" )); + window.$vue_app_name = window.GENIEMODEL = new Vue($( replace(vue_app, "'$(Stipple.UNDEFINED_PLACEHOLDER)'"=>Stipple.UNDEFINED_VALUE) )); + } // end of initStipple + + " + + , + + " + + function initWatchers(){ + " + + , + join( + [Stipple.watch(string("window.", vue_app_name), field, Stipple.channel_js_name, debounce, 0, model) for field in fieldnames(Stipple.get_concrete_type(M)) + if Stipple.has_frontend_watcher(field, model)] + ) + , + + " + } // end of initWatchers + + " + + , + + """ + + window.parse_payload = function(payload){ + if (payload.key) { + window.$(vue_app_name).updateField(payload.key, payload.value); + } + } + + function app_ready() { + $vue_app_name.channel_ = window.CHANNEL; + $vue_app_name.isready = true; + Genie.Revivers.addReviver(window.$(vue_app_name).revive_jsfunction); + $(transport == Genie.WebChannels && + " + try { + if (Genie.Settings.webchannels_keepalive_frequency > 0) { + clearInterval($vue_app_name.keepalive_interval); + $vue_app_name.keepalive_interval = setInterval(keepalive, Genie.Settings.webchannels_keepalive_frequency); + } + } catch (e) { + if (Genie.Settings.env === 'dev') { + console.error('Error setting WebSocket keepalive interval: ' + e); + } + } + ") + + if (Genie.Settings.env === 'dev') { + console.info('App starting'); + } + }; + + if ( window.autorun === undefined || window.autorun === true ) { + initStipple('#$vue_app_name'); + initWatchers(); + + Genie.WebChannels.subscriptionHandlers.push(function(event) { + app_ready(); + }); + } + """ + ) + + output = repr(output) + output[2:prevind(output, lastindex(output))] +end + #===# function quote_replace(s::String) diff --git a/src/Stipple.jl b/src/Stipple.jl index e34924e..4eb9b3c 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -20,6 +20,7 @@ const USE_MODEL_STORAGE = Ref(true) const PRECOMPILE = Ref(false) import MacroTools +import Pkg.TOML function use_model_storage() USE_MODEL_STORAGE[] @@ -681,6 +682,26 @@ function routename(::Type{M}) where M<:ReactiveModel replace(s, r"[^0-9a-zA-Z_]+" => "") end +function gb_stipple_dir() + gbdir = joinpath(Base.DEPOT_PATH[1], "geniebuilder") + replace(strip(read(`julia --project=$gbdir -E 'dirname(dirname(Base.find_package("Stipple")))'`, String), ['"', '\n']), "\\\\" =>'/') +end + +function gb_compat_deps(::Type{M}) where M <: ReactiveModel + get(ENV, "GB_ROUTES", "false") == "true" && return + basedir = gb_stipple_dir() + remote_assets_config = deepcopy(Stipple.assets_config) + remote_assets_config.version = TOML.parsefile(joinpath(basedir, "Project.toml"))["version"] + Genie.Assets.add_fileroute(remote_assets_config, "stipplecore.css"; basedir) + Genie.Assets.add_fileroute(remote_assets_config, "stipplecore.js"; basedir) + Genie.Assets.add_fileroute(remote_assets_config, "vue.js"; basedir) + Genie.Assets.add_fileroute(remote_assets_config, "vue_filters.js"; basedir) + Genie.Router.route(Genie.Assets.asset_route(remote_assets_config, :js, file = vm(M))) do + Stipple.Elements.vue2_integration(M) |> Genie.Renderer.Js.js + end + ENV["GB_ROUTES"] = true +end + function stipple_deps(::Type{M}, vue_app_name, debounce, throttle, core_theme, endpoint, transport)::Function where {M<:ReactiveModel} () -> begin if ! Genie.Assets.external_assets(assets_config) @@ -691,6 +712,8 @@ function stipple_deps(::Type{M}, vue_app_name, debounce, throttle, core_theme, e end end + haskey(ENV, "GB_JULIA_PATH") && gb_compat_deps(M) + [ if ! Genie.Assets.external_assets(assets_config) Genie.Renderer.Html.script(src = Genie.Assets.asset_path(assets_config, :js, file = vue_app_name), defer = true)