From c00504764d12ac16099c3946f053f100bb6a6957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pere=20Gime=CC=81nez?= Date: Thu, 23 Mar 2023 11:49:06 +0100 Subject: [PATCH 001/265] docstrings --- src/ReactiveTools.jl | 211 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 210 insertions(+), 1 deletion(-) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index 39840dfd..0902988c 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -173,6 +173,13 @@ end #===# +""" +```julia +@clear +``` + +Deletes all reactive variables and code in a model. +""" macro clear() delete_bindings!(__module__) delete_handlers!(__module__) @@ -191,10 +198,24 @@ macro clear(args...) REACTIVE_STORAGE[__module__] end +""" +```julia +@clear_vars +``` + +Deletes all reactive variables in a model. +""" macro clear_vars() delete_bindings!(__module__) end +""" +```julia +@clear_handlers +``` + +Deletes all reactive code handlers in a model. +""" macro clear_handlers() delete_handlers!(__module__) end @@ -246,6 +267,29 @@ macro model() end) end +""" +```julia +@app(expr) +``` + +Sets up and enables the reactive variables and code provided in the expression `expr`. + +**Usage** + +The code block passed to @app implements the app's logic, handling the states of the UI components and the code that is executed when these states are altered. + +```julia +@app begin + # reactive variables + @in N = 0 + @out result = 0 + # reactive code to be executed when N changes + @onchange N begin + result = 10*N + end +end +``` +""" macro app(expr) delete_bindings!(__module__) delete_handlers!(__module__) @@ -326,6 +370,56 @@ end # @in a::Vector = [1, 2, 3] # @in a::Vector{Int} = [1, 2, 3] +# the @in, @out and @private macros below are defined so a docstring can be attached +# the actual macro definition is done in the for loop further down +""" +```julia +@in(expr) +``` + +Declares a reactive variable that is public and can be written to from the UI. + +**Usage** +```julia +@app begin + @in N = 0 +end +``` +""" +macro in end + +""" +```julia +@out(expr) +``` + +Declares a reactive variable that is public and readonly. + +**Usage** +```julia +@app begin + @out N = 0 +end +``` +""" +macro out end + +""" +```julia +@private(expr) +``` + +Declares a non-reactive variable that cannot be accessed by UI code. + +**Usage** +```julia +@app begin + @private N = 0 +end +``` +""" +macro private end + for (fn, mode) in [(:in, :PUBLIC), (:out, :READONLY), (:jsnfn, :JSFUNCTION), (:private, :PRIVATE)] fn! = Symbol(fn, "!") Core.eval(@__MODULE__, quote @@ -654,6 +748,30 @@ function get_known_vars(::Type{M}) where M<:ReactiveModel reactive_vars, non_reactive_vars end +""" +```julia +@onchange(var, expr) +``` +Declares a reactive update such that when a reactive variable changes `expr` is executed. + +**Usage** + +This macro watches a list of variables and defines a code block that is executed when the variables change. + +```julia +@app begin + # reactive variables taking their value from the UI + @in N = 0 + @in M = 0 + @out result = 0 + # reactive code to be executed when N changes + @onchange N M begin + result = 10*N*M + end +end +``` + +""" macro onchange(var, expr) quote @onchange $__module__ $var $expr @@ -707,6 +825,30 @@ macro onchangeany(var, expr) end |> esc end +""" +```julia +@onbutton +``` +Declares a reactive update that executes `expr` when a button is pressed in the UI. + +**Usage** +Define a click event listener with `@click`, and the handler with `@onbutton`. + +```julia +@app begin + @in press = false + @onbutton press begin + println("Button presed!") + end +end + +ui() = btn("Press me", @click(:press)) + +@page("/", ui) +``` + + +""" macro onbutton(var, expr) quote @onbutton $__module__ $var $expr @@ -762,6 +904,18 @@ macro page(url, view, layout) :(@page($url, $view, $layout, () -> @eval($__module__, @init()))) |> esc end +""" +```julia +@page(url, view) +``` +Registers a new page with source in `view` to be rendered at the route `url`. + +**Usage** + +```julia +@page("/", "view.html") +``` +""" macro page(url, view) :(@page($url, $view, Stipple.ReactiveTools.DEFAULT_LAYOUT())) |> esc end @@ -860,6 +1014,61 @@ macro event(M, eventname, expr) end |> esc end +""" +```julia +@event(event, expr) +``` +Executes the code in `expr` when a specific `event` is triggered by a UI component. + +**Usage** + +Define an event trigger such as a click, keypress or file upload for a component using the @on macro. Then, define the handler for the event with @event. + + +**Examples** + +Keypress: + + +```julia +@app begin + @event :keypress begin + println("The Enter key has been pressed") + end +end + +ui() = textfield(class = "q-my-md", "Input", :input, hint = "Please enter some words", @on("keyup.enter", :keypress)) + +@page("/", ui) +``` + +======= +```julia + +``` +File upload: + +```julia +@app begin + @event :uploaded begin + println("Files have been uploaded!") + end +end + +ui() = uploader("Upload files", url = "/upload" , method="POST", @on(:uploaded, :uploaded), autoupload=true) + +route("/upload", method=POST) do + # process uploaded files +end + +@page("/", ui) +``` + +```julia +julia> print(ui()) +Upload files +``` +""" macro event(event, expr) quote @event Stipple.@type() $event $expr @@ -931,4 +1140,4 @@ macro notify(args...) end |> esc end -end \ No newline at end of file +end From f9df57171edf555129ce54d29a5294f583032b88 Mon Sep 17 00:00:00 2001 From: PGimenez Date: Tue, 18 Apr 2023 19:53:07 +0200 Subject: [PATCH 002/265] meta tags for default layout (#192) --- src/ReactiveTools.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index 39840dfd..bdf70d7c 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -30,12 +30,14 @@ const REACTIVE_STORAGE = LittleDict{Module,LittleDict{Symbol,Expr}}() const HANDLERS = LittleDict{Module,Vector{Expr}}() const TYPES = LittleDict{Module,Union{<:DataType,Nothing}}() -function DEFAULT_LAYOUT(; title::String = "Genie App") +function DEFAULT_LAYOUT(; title::String = "Genie App", meta::Dict{<:AbstractString,<:AbstractString} = Dict("og:title" => "Genie App")) + tags = Genie.Renderers.Html.for_each(x -> """\n """, meta) """ + $tags <% Stipple.sesstoken() %> $title <% if isfile(joinpath(Genie.config.server_document_root, "css", "genieapp.css")) %> @@ -931,4 +933,4 @@ macro notify(args...) end |> esc end -end \ No newline at end of file +end From 8d4a8eadf64c13fa9bc7ec284139f16963c6f03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Tue, 25 Apr 2023 23:31:20 +0200 Subject: [PATCH 003/265] new parsers for filling Arrays, e.g. Vector to Matrix --- src/stipple/parsers.jl | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/stipple/parsers.jl b/src/stipple/parsers.jl index ee71f309..e7bd5792 100644 --- a/src/stipple/parsers.jl +++ b/src/stipple/parsers.jl @@ -13,12 +13,19 @@ function stipple_parse(::Type{T1}, value::T2) where {T1 <: Integer, T2 <: Number round(T1, value) end -function stipple_parse(::Type{T1}, value::T2) where {T1 <: AbstractArray, T2 <: AbstractArray} +# AbstractArray's of same dimension +function stipple_parse(::Type{T1}, value::T2) where {N, T1 <: AbstractArray{<:Any, N}, T2 <: AbstractArray{<:Any, N}} T1(stipple_parse.(eltype(T1), value)) end -function stipple_parse(::Type{T}, value) where T <: AbstractArray - convert(T, eltype(T)[value]) +# atomic value to Array +function stipple_parse(::Type{T}, value) where {N, T <: AbstractArray{<:Any, N}} + convert(T, Array{eltype(T), N}(reshape([value], fill(1, N)...))) +end + +# Vector to Matrix, in particular Vector of Vectors to Matrix +function stipple_parse(::Type{T1}, value::T2) where {T1 <: AbstractArray{<:Any, 2}, T2 <: AbstractArray{<:Any, 1}} + reduce(hcat, stipple_parse.(Vector{eltype(T1)}, value)) end function stipple_parse(::Type{T}, value) where T <: AbstractRange @@ -29,18 +36,21 @@ function stipple_parse(::Type{T}, v::T) where {T} v::T end +# String to Symbol function stipple_parse(::Type{Symbol}, s::String) Symbol(s) end - +# untyped Dicts to typed Dict's function stipple_parse(::Type{<:AbstractDict{K, V}}, value::AbstractDict{String, <:Any}) where {K, V} Dict( zip(Vector{K}(stipple_parse(Vector{K}, collect(keys(value)))), stipple_parse(Vector{V}, collect(values(value)))) ) end -function stipple_parse(::Type{Integer}, value::String) - stipple_parse(Int, value) +# String to Integer +function stipple_parse(::Type{T}, value::String) where T <: Integer + Base.parse(T, value) end -function stipple_parse(::Type{AbstractFloat}, value::String) - stipple_parse(Float64, value) +#String to AbstractFloat +function stipple_parse(::Type{T}, value::String) where T<:AbstractFloat + Base.parse(T, value) end \ No newline at end of file From b058d532fd4808f9e63abebf22b9e9d4196a5a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Fri, 28 Apr 2023 15:07:57 +0200 Subject: [PATCH 004/265] remove fieldname from Stipple.render() where possible --- src/stipple/rendering.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/stipple/rendering.jl b/src/stipple/rendering.jl index 66ef622a..231d47d0 100644 --- a/src/stipple/rendering.jl +++ b/src/stipple/rendering.jl @@ -67,7 +67,11 @@ end Default rendering of value types. Specialize `Stipple.render` to define custom rendering for your types. """ -function Stipple.render(val::T, fieldname::Union{Symbol,Nothing} = nothing) where {T} +function Stipple.render(val::T, fieldname::Union{Symbol,Nothing}) where {T} + Stipple.render(val) +end + +function Stipple.render(val::T) where {T} val end From e604372fbec754385fab41be773aec9c21310dcb Mon Sep 17 00:00:00 2001 From: abhimanyuaryan <8083613+AbhimanyuAryan@users.noreply.github.com> Date: Fri, 28 Apr 2023 22:43:40 +0530 Subject: [PATCH 005/265] bumping genie to 5.17 and stipple to 0.26.4 --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 02e2d3fa..6b167e9e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.26.3" +version = "0.26.4" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" @@ -24,7 +24,7 @@ StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" [compat] FilePathsBase = "0.9" -Genie = "5.15.3" +Genie = "5.17.0" GenieSession = "1" GenieSessionFileSession = "1" JSON3 = "1.9" From a70f8e82b8839b2ab31d90bedef5f6fe6ab25ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Sun, 30 Apr 2023 22:41:50 +0200 Subject: [PATCH 006/265] avoid macro evaluation in Stipple --- src/ReactiveTools.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index bdf70d7c..3178e27b 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -271,7 +271,7 @@ end function binding(expr::Expr, m::Module, @nospecialize(mode::Any = nothing); source = nothing, reactive = true) (m == @__MODULE__) && return nothing - intmode = @eval Stipple $mode + intmode = mode isa Integer ? Int(mode) : @eval Stipple.$mode init_storage(m) var, field_expr = parse_expression!(expr, reactive ? mode : nothing, source, m) @@ -285,7 +285,7 @@ function binding(expr::Expr, m::Module, @nospecialize(mode::Any = nothing); sour end function binding(expr::Expr, storage::LittleDict{Symbol, Expr}, @nospecialize(mode::Any = nothing); source = nothing, reactive = true, m::Module) - intmode = @eval Stipple $mode + intmode = mode isa Integer ? Int(mode) : @eval Stipple.$mode var, field_expr = parse_expression!(expr, reactive ? mode : nothing, source, m) storage[var] = field_expr From 7c99dcd2202c99bd7f1237e31f70785adbae5da5 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Tue, 2 May 2023 18:03:02 +0200 Subject: [PATCH 007/265] remove eval in setmode!(), make mode__ field type stable --- src/ReactiveTools.jl | 21 +++++++++++++++++---- src/Stipple.jl | 2 +- src/stipple/reactivity.jl | 6 +++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index 3178e27b..c2790dc5 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -118,8 +118,21 @@ end function Stipple.setmode!(expr::Expr, mode::Int, fieldnames::Symbol...) fieldname in [Stipple.CHANNELFIELDNAME, :modes__] && return - - d = eval(expr.args[2]) + expr.args[2] isa Expr && expr.args[2].args[1] == :(Stipple._deepcopy) && (expr.args[2] = expr.args[2].args[2]) + + d = if expr.args[2] isa LittleDict + copy(expr.args[2]) + elseif expr.args[2] isa QuoteNode + expr.args[2].value + else # isa Expr generating a LittleDict (hopefully ...) + expr.args[2].args[1].args[1] == :(Stipple.LittleDict) || expr.args[2].args[1].args[1] == :(LittleDict) || error("Unexpected error while setting access properties of app variables") + + d = LittleDict{Symbol, Int}() + for p in expr.args[2].args[2:end] + push!(d, p.args[2].value => p.args[3]) + end + d + end for fieldname in fieldnames mode == PUBLIC ? delete!(d, fieldname) : d[fieldname] = mode end @@ -416,7 +429,7 @@ macro init(modeltype) local initfn = if isdefined($__module__, :init_from_storage) $__module__.init_from_storage else - $__module__.init + Stipple.init end local handlersfn = if isdefined($__module__, :__GF_AUTO_HANDLERS__) if length(methods($__module__.__GF_AUTO_HANDLERS__)) == 0 @@ -439,7 +452,7 @@ end macro init() quote let type = Stipple.@type - @init(type) + Stipple.ReactiveTools.@init(type) end end |> esc end diff --git a/src/Stipple.jl b/src/Stipple.jl index abad7f2d..ff7ec437 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -321,7 +321,7 @@ function init_storage() LittleDict{Symbol, Expr}( CHANNELFIELDNAME => :($(Stipple.CHANNELFIELDNAME)::$(Stipple.ChannelName) = Stipple.channelfactory()), - :modes__ => :(modes__::Stipple.LittleDict{Symbol, Any} = Stipple.LittleDict{Symbol, Any}()), + :modes__ => :(modes__::Stipple.LittleDict{Symbol, Int} = Stipple.LittleDict{Symbol, Int}()), :isready => :(isready::Stipple.R{Bool} = false), :isprocessing => :(isprocessing::Stipple.R{Bool} = false) ) diff --git a/src/stipple/reactivity.jl b/src/stipple/reactivity.jl index e848f20e..0c1610ef 100644 --- a/src/stipple/reactivity.jl +++ b/src/stipple/reactivity.jl @@ -176,8 +176,8 @@ function model_to_storage(::Type{T}, prefix = "", postfix = "") where T# <: Reac end function merge_storage(storage_1::AbstractDict, storage_2::AbstractDict; keep_channel = true) - m1 = eval(haskey(storage_1, :modes__) ? storage_1[:modes__].args[end] : LittleDict{Symbol, Any}()) - m2 = eval(haskey(storage_2, :modes__) ? storage_2[:modes__].args[end] : LittleDict{Symbol, Any}()) + m1 = eval(haskey(storage_1, :modes__) ? storage_1[:modes__].args[end] : LittleDict{Symbol, Int}()) + m2 = eval(haskey(storage_2, :modes__) ? storage_2[:modes__].args[end] : LittleDict{Symbol, Int}()) modes = merge(m1, m2) keep_channel && haskey(storage_2, :channel__) && (storage_2 = delete!(copy(storage_2), :channel__)) @@ -192,7 +192,7 @@ function merge_storage(storage_1::AbstractDict, storage_2::AbstractDict; keep_ch end end storage = merge(storage_1, storage_2) - storage[:modes__] = :(modes__::Stipple.LittleDict{Symbol, Any} = $modes) + storage[:modes__] = :(modes__::Stipple.LittleDict{Symbol, Int} = $modes) storage end From 33189c2535eb51e01f3bc92bf19590421d5576bd Mon Sep 17 00:00:00 2001 From: hhaensel <31985040+hhaensel@users.noreply.github.com> Date: Wed, 3 May 2023 23:45:01 +0200 Subject: [PATCH 008/265] add macro `@using_except` to avoid name clashes of div, view, mark, time, download, render, Headers (#196) --- src/Stipple.jl | 53 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Stipple.jl b/src/Stipple.jl index ff7ec437..e2f9d3f9 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -15,11 +15,60 @@ existing Vue.js libraries. """ module Stipple +""" +@using_except(expr) + +using statement while excluding certain names + +### Example +``` +using Parent.MyModule: x, y +``` +will import all names from Parent.MyModule except `x` and `y`. Currently suports only a single module. +""" +macro using_except(expr) + # check validity + expr isa Expr && (expr.args[1] == :(:) || (expr.args[1].head == :call && expr.args[1].args[1] == :(:))) || return + + # determine module name and list of excluded symbols + m, excluded = expr.args[1] == :(:) ? (expr.args[2], Symbol[expr.args[3]]) : (expr.args[1].args[2], Symbol[s for s in vcat([expr.args[1].args[3]], expr.args[2:end])]) + + # convert m.args to list of Symbols + if m isa Expr + m.args[2] = m.args[2].value + while m.args[1] isa Expr + pushfirst!(m.args, m.args[1].args[1]); + m.args[2] = m.args[2].args[2].value + end + end + + m_name = m isa Expr ? m.args[end] : m + + # as a first step use only the module name + # by constructing `using Parent.MyModuleName: MyModule` + expr = :(using dummy1: dummy2) + expr.args[1].args[1].args = m isa Expr ? m.args : Any[m] + expr.args[1].args[2].args[1] = m_name + # execute the using statement + M = Core.eval(__module__, :($expr; $m_name)) + # determine list of all exported names + nn = filter!(x -> Base.isexported(M, x) && ! (x ∈ excluded) , names(M; all = true, imported = true)) + # convert the list of symbols to list of imported names + args = [:($(Expr(:., n))) for n in nn] + # re-use previous expression and insert the names to be imported + expr.args[1].args = pushfirst!(args, expr.args[1].args[1]) + + @debug(expr) + expr +end + + using Logging, Mixers, Random, Reexport, Requires, Dates @reexport using Observables -@reexport using Genie -@reexport using Genie.Renderer.Html +@reexport @using_except Genie: download +import Genie.Router.download +@reexport @using_except Genie.Renderer.Html: mark, div, time, view, render, Headers @reexport using JSON3 @reexport using StructTypes @reexport using Parameters From a7cf07cb8ca9cc08fe619288e259ac4255bcba51 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Wed, 3 May 2023 23:47:20 +0200 Subject: [PATCH 009/265] set version v0.26.5 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6b167e9e..1d2ca830 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.26.4" +version = "0.26.5" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From f34539ec7bad05d48d91f8195e722ac2a58a6898 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Fri, 5 May 2023 10:34:41 +0200 Subject: [PATCH 010/265] export render() after it was unintenionally removed from exports --- src/Stipple.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Stipple.jl b/src/Stipple.jl index e2f9d3f9..545f3930 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -69,6 +69,7 @@ using Logging, Mixers, Random, Reexport, Requires, Dates @reexport @using_except Genie: download import Genie.Router.download @reexport @using_except Genie.Renderer.Html: mark, div, time, view, render, Headers +export render @reexport using JSON3 @reexport using StructTypes @reexport using Parameters From 9b6a55cbd4f3fce27d5e0fd1d64afc5942ed5165 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Fri, 5 May 2023 10:35:11 +0200 Subject: [PATCH 011/265] set version v0.26.6 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1d2ca830..6c762e5d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.26.5" +version = "0.26.6" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From ca97fb0ddb5338e033fd0027684cb7ebad82f781 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Fri, 5 May 2023 18:49:33 +0200 Subject: [PATCH 012/265] prefix VERSION with Base., check for isdefined in @using_except --- src/Stipple.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Stipple.jl b/src/Stipple.jl index 545f3930..6a281e43 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -49,12 +49,16 @@ macro using_except(expr) expr = :(using dummy1: dummy2) expr.args[1].args[1].args = m isa Expr ? m.args : Any[m] expr.args[1].args[2].args[1] = m_name + # execute the using statement M = Core.eval(__module__, :($expr; $m_name)) + # determine list of all exported names - nn = filter!(x -> Base.isexported(M, x) && ! (x ∈ excluded) , names(M; all = true, imported = true)) + nn = filter!(x -> Base.isexported(M, x) && ! (x ∈ excluded) && isdefined(M, x), names(M; all = true, imported = true)) + # convert the list of symbols to list of imported names args = [:($(Expr(:., n))) for n in nn] + # re-use previous expression and insert the names to be imported expr.args[1].args = pushfirst!(args, expr.args[1].args[1]) @@ -843,7 +847,7 @@ macro kwredef(expr) esc(quote Base.@kwdef $expr $T_old = $T_new - if VERSION < v"1.8-" + if Base.VERSION < v"1.8-" $curly ? $T_new.body.name.name = $(QuoteNode(T_old)) : $T_new.name.name = $(QuoteNode(T_old)) # fix the name end From aacd3065445111fb44229d95197ee7a4f7e01f1f Mon Sep 17 00:00:00 2001 From: hhaensel Date: Fri, 5 May 2023 18:50:16 +0200 Subject: [PATCH 013/265] set version v0.26.7 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6c762e5d..848f9244 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.26.6" +version = "0.26.7" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From 2bb4ebc9694d3c5b46641137226e32d917b2ddd5 Mon Sep 17 00:00:00 2001 From: Adrian Salceanu Date: Mon, 8 May 2023 14:47:14 +0200 Subject: [PATCH 014/265] support for basepath in default layout --- Project.toml | 4 ++-- src/ReactiveTools.jl | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Project.toml b/Project.toml index 02e2d3fa..289a87c7 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.26.3" +version = "0.26.8" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" @@ -24,7 +24,7 @@ StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" [compat] FilePathsBase = "0.9" -Genie = "5.15.3" +Genie = "5.18" GenieSession = "1" GenieSessionFileSession = "1" JSON3 = "1.9" diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index 39840dfd..0f06c751 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -39,11 +39,11 @@ function DEFAULT_LAYOUT(; title::String = "Genie App") <% Stipple.sesstoken() %> $title <% if isfile(joinpath(Genie.config.server_document_root, "css", "genieapp.css")) %> - + <% else %> <% end %> <% if isfile(joinpath(Genie.config.server_document_root, "css", "autogenerated.css")) %> - + <% else %> <% end %> + $(join(head_content, "\n "))
From 21fdd3d556aadd3980be436a64b3171e15eee8f8 Mon Sep 17 00:00:00 2001 From: Yakir Luc Gagnon <12.yakir@gmail.com> Date: Thu, 18 Jan 2024 09:57:58 +0100 Subject: [PATCH 146/265] Update ReactiveTools.jl Added a tiny note about specifying an explicit app in the `@page` macro. --- src/ReactiveTools.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index af976e9e..6c92c75a 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -1004,6 +1004,8 @@ Registers a new page with source in `view` to be rendered at the route `url`. ```julia @page("/", "view.html") + +@page("/", ui; model = MyApp) # for specifying an explicit app ``` """ macro page(expressions...) From c7cbcb780b96dc81aadbef1ce07fca322718746b Mon Sep 17 00:00:00 2001 From: hhaensel Date: Thu, 18 Jan 2024 11:46:14 +0100 Subject: [PATCH 147/265] fix channel issue --- src/ReactiveTools.jl | 6 ++--- src/Stipple.jl | 12 +++------- src/stipple/reactivity.jl | 3 ++- test/runtests.jl | 50 ++++++++++++++++++++++----------------- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index af976e9e..c94a4ebb 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -612,7 +612,7 @@ macro init(args...) end instance = let model = initfn($(init_args...)) - new_handlers ? Base.invokelatest(handlersfn, model) : handlersfn(model) + new_handlers ? Base.invokelatest(handlersfn, model) : handlersfn(model) end for p in Stipple.Pages._pages p.context == $__module__ && (p.model = instance) @@ -836,7 +836,7 @@ function get_known_vars(M::Module) reactive_vars = Symbol[] non_reactive_vars = Symbol[] for (k, v) in REACTIVE_STORAGE[M] - k in [:channel__, :modes__] && continue + k in Stipple.INTERNALFIELDS && continue is_reactive = startswith(string(Stipple.split_expr(v)[2]), r"(Stipple\.)?R(eactive)?($|{)") push!(is_reactive ? reactive_vars : non_reactive_vars, k) end @@ -848,7 +848,7 @@ function get_known_vars(::Type{M}) where M<:ReactiveModel reactive_vars = Symbol[] non_reactive_vars = Symbol[] for (k, v) in zip(fieldnames(CM), fieldtypes(CM)) - k in [:channel__, :modes__] && continue + k in Stipple.INTERNALFIELDS && continue push!(v <: Reactive ? reactive_vars : non_reactive_vars, k) end reactive_vars, non_reactive_vars diff --git a/src/Stipple.jl b/src/Stipple.jl index fa7fc74d..eb2f9aaa 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -401,8 +401,7 @@ function init_storage() ch = channelfactory() LittleDict{Symbol, Expr}( - CHANNELFIELDNAME => - :($(Stipple.CHANNELFIELDNAME)::$(Stipple.ChannelName) = $ch), + CHANNELFIELDNAME => :($(Stipple.CHANNELFIELDNAME)::$(Stipple.ChannelName) = $ch), :modes__ => :(modes__::Stipple.LittleDict{Symbol,Int} = Stipple.LittleDict{Symbol,Int}()), :isready => :(isready::Stipple.R{Bool} = false), :isprocessing => :(isprocessing::Stipple.R{Bool} = false), @@ -455,13 +454,8 @@ function init(t::Type{M}; transport == Genie.WebChannels || (Genie.config.websockets_server = false) ok_response = "OK" - channel = if channel !== nothing - setchannel(model, channel) - elseif hasproperty(model, CHANNELFIELDNAME) - getchannel(model) - else - setchannel(model, channel) - end + channel === nothing && (channel = channelfactory()) + setchannel(model, channel) # make sure we store the channel name in the model Stipple.ModelStorage.Sessions.store(model) diff --git a/src/stipple/reactivity.jl b/src/stipple/reactivity.jl index c2fe41e0..70f61e4b 100644 --- a/src/stipple/reactivity.jl +++ b/src/stipple/reactivity.jl @@ -172,10 +172,11 @@ function setchannel(m::M, value) where {M<:ReactiveModel} end const AUTOFIELDS = [:isready, :isprocessing, :fileuploads] # not DRY but we need a reference to the auto-set fields +const INTERNALFIELDS = [CHANNELFIELDNAME, :modes__] # not DRY but we need a reference to the auto-set fields @pour reactors begin - modes__::LittleDict{Symbol, Int} = LittleDict(:modes__ => PRIVATE, :channel__ => PRIVATE) channel__::Stipple.ChannelName = Stipple.channelfactory() + modes__::LittleDict{Symbol, Int} = LittleDict(:modes__ => PRIVATE, :channel__ => PRIVATE) isready::Stipple.R{Bool} = false isprocessing::Stipple.R{Bool} = false channel_::String = "" # not sure what this does if it's empty diff --git a/test/runtests.jl b/test/runtests.jl index d5d5211b..a9a20fae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,8 +5,8 @@ using Test version = Genie.Assets.package_version(Stipple) -function string_get(x) - String(HTTP.get(x, retries = 0, status_exception = false).body) +function string_get(x; kwargs...) + String(HTTP.get(x, retries = 0, status_exception = false; kwargs...).body) end function get_channel(s::String) @@ -42,9 +42,9 @@ end # channels have to be different @test model.channel__ != model2.channel__ - + # check whether fields are correctly defined - @test propertynames(model) == (:channel__, :modes__, :isready, :isprocessing, :i, :s) + @test propertynames(model) == tuple(Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS..., :i, :s) # check reactivity model.i[] = 20 @@ -69,7 +69,7 @@ end end model = TestApp |> init |> handlers - @test propertynames(model) == (:channel__, :modes__, :isready, :isprocessing, :i, :s, :j, :t, :mixin_j, :mixin_t, :pre_j_post, :pre_t_post) + @test propertynames(model) == tuple(Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS..., :i, :s, :j, :t, :mixin_j, :mixin_t, :pre_j_post, :pre_t_post) end using Stipple.ReactiveTools @@ -84,14 +84,14 @@ using Stipple.ReactiveTools end end - model = TestApp2 |> init |> handlers - model2 = TestApp2 |> init |> handlers + model = @init TestApp2 + model2 = @init TestApp2 # channels have to be different @test model.channel__ != model2.channel__ # check whether fields are correctly defined - @test propertynames(model) == (:channel__, :modes__, :isready, :isprocessing, :i, :s) + @test propertynames(model) == tuple(Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS..., :i, :s) # check reactivity model.i[] = 20 @@ -113,7 +113,7 @@ end end @eval model = TestApp |> init |> handlers - @test propertynames(model) == (:channel__, :modes__, :isready, :isprocessing, :i, :s, :j, :t, :mixin_j, :mixin_t, :pre_j_post, :pre_t_post) + @test propertynames(model) == tuple(Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS..., :i, :s, :j, :t, :mixin_j, :mixin_t, :pre_j_post, :pre_t_post) # check reactivity @eval model.i[] = 20 @@ -132,12 +132,12 @@ end @eval model = @init @eval model2 = @init - + # channels have to be different @eval @test model.channel__ != model2.channel__ # check whether fields are correctly defined - @eval @test propertynames(model) == (:channel__, :modes__, :isready, :isprocessing, :i2, :s2) + @eval @test propertynames(model) == tuple(Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS..., :i2, :s2) # check reactivity @eval model.i2[] = 20 @@ -159,7 +159,7 @@ end end @eval model = @init - @eval @test propertynames(model) == (:channel__, :modes__, :isready, :isprocessing, :i3, :s3, :j, :t, :mixin_j, :mixin_t, :pre_j_post, :pre_t_post) + @eval @test propertynames(model) == tuple(Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS..., :i3, :s3, :j, :t, :mixin_j, :mixin_t, :pre_j_post, :pre_t_post) @eval model.i3[] = 20 @test model.s3[] == "20" @@ -243,12 +243,15 @@ end s1 = string_get("http://localhost:$port/") s2 = string_get("http://localhost:$port/") - - s3 = string_get("http://localhost:$port/static") + s3 = string_get("http://localhost:$port/", cookies = false) + s4 = string_get("http://localhost:$port/static") + s5 = string_get("http://localhost:$port/static") + s6 = string_get("http://localhost:$port/static", cookies = false) - @test get_channel(s2) != get_channel(s1) - @test get_channel(s3) == get_channel(s4) + @test get_channel(s2) == get_channel(s1) + @test get_channel(s3) != get_channel(s1) + @test get_channel(s4) == get_channel(s5) == get_channel(s6) @clear_cache down() @@ -300,13 +303,16 @@ end s1 = string_get("http://localhost:$port/") s2 = string_get("http://localhost:$port/") + s3 = string_get("http://localhost:$port/", cookies = false) + + s4 = string_get("http://localhost:$port/static") + s5 = string_get("http://localhost:$port/static") + s6 = string_get("http://localhost:$port/static", cookies = false) - s3 = string_get("http://localhost:$port/static1") - s4 = string_get("http://localhost:$port/static1") - - @test get_channel(s2) != get_channel(s1) - @test get_channel(s3) == get_channel(s4) - + @test get_channel(s2) == get_channel(s1) + @test get_channel(s3) != get_channel(s1) + @test get_channel(s4) == get_channel(s5) == get_channel(s6) + @clear_cache MyApp down() end From af9a24120d551d884853055adf27edc2587448eb Mon Sep 17 00:00:00 2001 From: hhaensel Date: Thu, 18 Jan 2024 12:00:21 +0100 Subject: [PATCH 148/265] remove :channel_ field from init_storage --- src/Stipple.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Stipple.jl b/src/Stipple.jl index eb2f9aaa..704a2450 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -405,7 +405,6 @@ function init_storage() :modes__ => :(modes__::Stipple.LittleDict{Symbol,Int} = Stipple.LittleDict{Symbol,Int}()), :isready => :(isready::Stipple.R{Bool} = false), :isprocessing => :(isprocessing::Stipple.R{Bool} = false), - :channel_ => :(channel_::String = $ch), :fileuploads => :(fileuploads::Stipple.R{Dict{AbstractString,AbstractString}} = Dict{AbstractString,AbstractString}()) ) end From 3821bc4e3cb9fc9bc2d43ea9c81fdae0c1505c60 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Wed, 17 Jan 2024 11:39:53 +0100 Subject: [PATCH 149/265] add printing for ReactiveModel --- src/Stipple.jl | 1 + src/stipple/print.jl | 57 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/stipple/print.jl diff --git a/src/Stipple.jl b/src/Stipple.jl index fa7fc74d..3926114f 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -96,6 +96,7 @@ include("stipple/json.jl") include("stipple/undefined.jl") include("stipple/assets.jl") include("stipple/converters.jl") +include("stipple/print.jl") using .NamedTuples diff --git a/src/stipple/print.jl b/src/stipple/print.jl new file mode 100644 index 00000000..2b651410 --- /dev/null +++ b/src/stipple/print.jl @@ -0,0 +1,57 @@ +function print_object(io, obj::T, omit = nothing, compact = false) where T + # currently no different printing for compact = true ... + fields = [p for p in propertynames(obj)] + omit !== nothing && setdiff!(fields, omit) + + println(io, match(r"^#*([^!]+)", String(T.name.name)).captures[1]) + for field in fields + println(io, " $field: ", Observables.to_value(getproperty(obj, field))) + end +end + +function print_object(io, obj::T, omit = nothing, compact = false) where T <: ReactiveModel + fields = [p for p in propertynames(obj)] + omit !== nothing && setdiff!(fields, omit) + internal_or_auto = true + + println(io, "Instance of '" * match(r"^#*([^!]+)", String(T.name.name)).captures[1] * "'") + for fieldname in fields + field = getproperty(obj, fieldname) + fieldmode = isprivate(fieldname, obj) ? "private" : isreadonly(fieldname, obj) ? "out" : "in" + fieldtype = if field isa Reactive + if fieldname in AUTOFIELDS + "autofield, $fieldmode" + else + if internal_or_auto + println(io) + internal_or_auto = false + end + fieldmode + end + else + if fieldname in INTERNALFIELDS + "internal" + else + if internal_or_auto + println(io) + internal_or_auto = false + end + "$fieldmode, non-reactive" + end + end + + println(io, " $fieldname ($fieldtype): ", Observables.to_value(field)) + end +end + +# default show used by Array show +function Base.show(io::IO, obj::ReactiveModel) + compact = get(io, :compact, true) + print_object(io, obj, compact) +end + +# default show used by display() on the REPL +function Base.show(io::IO, mime::MIME"text/plain", obj::ReactiveModel) + compact = get(io, :compact, false) + print_object(io, obj, compact) +end \ No newline at end of file From f54b7dfcd8143d81585c53fbc746a644cbff4c2b Mon Sep 17 00:00:00 2001 From: hhaensel Date: Wed, 17 Jan 2024 12:01:25 +0100 Subject: [PATCH 150/265] show name of module for implicit models --- src/stipple/print.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/stipple/print.jl b/src/stipple/print.jl index 2b651410..eafccfa2 100644 --- a/src/stipple/print.jl +++ b/src/stipple/print.jl @@ -14,7 +14,11 @@ function print_object(io, obj::T, omit = nothing, compact = false) where T <: Re omit !== nothing && setdiff!(fields, omit) internal_or_auto = true - println(io, "Instance of '" * match(r"^#*([^!]+)", String(T.name.name)).captures[1] * "'") + app = match(r"^#*([^!]+)", String(T.name.name)).captures[1] + if app == "Main_ReactiveModel" && parentmodule(T) != Main + app = String(nameof(parentmodule(T))) + end + println(io, "Instance of '$app'") for fieldname in fields field = getproperty(obj, fieldname) fieldmode = isprivate(fieldname, obj) ? "private" : isreadonly(fieldname, obj) ? "out" : "in" From aaf114ce87f9a7c5f55551bdbf092d6296f5ac80 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Thu, 18 Jan 2024 12:23:42 +0100 Subject: [PATCH 151/265] set version v0.27.32 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index fd23d19c..3bc0668d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.31" +version = "0.27.32" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From d097be6ccc2a4901848d54b93adbb5d04856114e Mon Sep 17 00:00:00 2001 From: hhaensel Date: Thu, 18 Jan 2024 12:46:43 +0100 Subject: [PATCH 152/265] set version v0.27.33 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3bc0668d..ac89aca0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.32" +version = "0.27.33" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From 6f5ec075839c276b0b4d298c00876ce81703b779 Mon Sep 17 00:00:00 2001 From: Adrian Salceanu Date: Thu, 25 Jan 2024 07:30:55 +0100 Subject: [PATCH 153/265] Allow disabling model storage --- src/Stipple.jl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Stipple.jl b/src/Stipple.jl index fa7fc74d..93e382e1 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -17,6 +17,15 @@ module Stipple const PRECOMPILE = Ref(false) const ALWAYS_REGISTER_CHANNELS = Ref(true) +const USE_MODEL_STORAGE = Ref(true) + +""" +Disables the automatic storage and retrieval of the models in the session. +Useful for large models. +""" +def disable_model_storage() + USE_MODEL_STORAGE[] = false +end """ @using_except(expr) @@ -356,6 +365,9 @@ function channeldefault(::Type{M}) where M<:ReactiveModel end model_id = Symbol(Stipple.routename(M)) + + ! USE_MODEL_STORAGE[] && return nothing + stored_model = Stipple.ModelStorage.Sessions.GenieSession.get(model_id, nothing) stored_model === nothing ? nothing : getfield(stored_model, Stipple.CHANNELFIELDNAME) end @@ -464,7 +476,7 @@ function init(t::Type{M}; end # make sure we store the channel name in the model - Stipple.ModelStorage.Sessions.store(model) + USE_MODEL_STORAGE[] && Stipple.ModelStorage.Sessions.store(model) # add a timer that checks if the model is outdated and if so prepare the model to be garbage collected LAST_ACTIVITY[Symbol(getchannel(model))] = now() @@ -486,7 +498,7 @@ function init(t::Type{M}; client = transport == Genie.WebChannels ? Genie.WebChannels.id(Genie.Requests.wsclient()) : Genie.Requests.wtclient() try - haskey(payload, "sesstoken") && ! isempty(payload["sesstoken"]) && + haskey(payload, "sesstoken") && ! isempty(payload["sesstoken"]) && USE_MODEL_STORAGE[] && Genie.Router.params!(Stipple.ModelStorage.Sessions.GenieSession.PARAMS_SESSION_KEY, Stipple.ModelStorage.Sessions.GenieSession.load(payload["sesstoken"] |> Genie.Encryption.decrypt)) catch ex From 17bc48413fab4ea3c9330bed03c87cea4827ed00 Mon Sep 17 00:00:00 2001 From: Adrian Salceanu Date: Thu, 25 Jan 2024 13:04:10 +0100 Subject: [PATCH 154/265] Allow disabling model storage --- Project.toml | 2 +- src/ReactiveTools.jl | 2 +- src/Stipple.jl | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index ac89aca0..c1576fc8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.33" +version = "0.27.34" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index de6f7f10..32d5fc40 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -588,7 +588,7 @@ macro init(args...) local new_handlers = false local initfn = - if isdefined($__module__, :init_from_storage) + if isdefined($__module__, :init_from_storage) && Stipple.USE_MODEL_STORAGE[] $__module__.init_from_storage else Stipple.init diff --git a/src/Stipple.jl b/src/Stipple.jl index 942d6fa0..c169e5ca 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -23,8 +23,8 @@ const USE_MODEL_STORAGE = Ref(true) Disables the automatic storage and retrieval of the models in the session. Useful for large models. """ -def disable_model_storage() - USE_MODEL_STORAGE[] = false +function enable_model_storage(enable::Bool = true) + USE_MODEL_STORAGE[] = enable end """ @@ -97,7 +97,7 @@ export setchannel, getchannel isempty(methods(notify, Observables)) && (Base.notify(observable::AbstractObservable) = Observables.notify!(observable)) include("ParsingTools.jl") -include("ModelStorage.jl") +USE_MODEL_STORAGE[] && include("ModelStorage.jl") include("NamedTuples.jl") include("stipple/reactivity.jl") @@ -367,7 +367,7 @@ function channeldefault(::Type{M}) where M<:ReactiveModel model_id = Symbol(Stipple.routename(M)) - ! USE_MODEL_STORAGE[] && return nothing + USE_MODEL_STORAGE[] || return nothing stored_model = Stipple.ModelStorage.Sessions.GenieSession.get(model_id, nothing) stored_model === nothing ? nothing : getfield(stored_model, Stipple.CHANNELFIELDNAME) From a202ec737f581a6b5d6df8d64b75f33d62e646a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pere=20Gim=C3=A9nez?= Date: Thu, 25 Jan 2024 16:36:06 +0100 Subject: [PATCH 155/265] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 40196013..3c535126 100644 --- a/README.md +++ b/README.md @@ -152,8 +152,10 @@ end Genie.isrunning(:webserver) || up() ``` + ## Choosing the transport layer: WebSockets or HTTP + By default Stipple will attempt to use WebSockets for real time data sync between backend and frontend. However, in some cases WebSockets support might not be available on the host. In this case, Stipple can be switched to use regular HTTP for data sync, using frontend polling with AJAX (1s polling interval by default). From 88f36710e1d3b07e0a56f611ed7a84d008e9b40b Mon Sep 17 00:00:00 2001 From: hhaensel Date: Thu, 25 Jan 2024 22:40:17 +0100 Subject: [PATCH 156/265] add field-specific debouncing --- src/ReactiveTools.jl | 134 +++++++++++++++++++++++++++++++++++++++++++ src/Stipple.jl | 57 +++++++++++++++--- test/runtests.jl | 15 +++++ 3 files changed, 199 insertions(+), 7 deletions(-) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index de6f7f10..bee7ffb9 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -16,6 +16,9 @@ export @onchange, @onbutton, @event, @notify # definition of dependencies export @deps, @clear_deps +# definition of field-specific debounce times +export @debounce, @clear_debounce + # deletion export @clear, @clear_vars, @clear_handlers @@ -269,6 +272,137 @@ macro clear_route() :(Stipple.clear_route(Stipple.@type)) |> esc end +function _prepare(fieldname) + if fieldname isa Symbol + fieldname = QuoteNode(fieldname) + else + if fieldname isa Expr && fieldname.head == :tuple + for (i, x) in enumerate(fieldname.args) + x isa Symbol && (fieldname.args[i] = QuoteNode(x)) + end + end + end + fieldname +end + +""" + @debounce fieldname ms + + @debounce App fieldname ms + +Set field-specific debounce time in ms + +### Example +#### Implicit apps +``` +@app begin + @out quick = 12 + @out slow = 12 + @in s = "Hello" +end + +# no debouncing for fast messaging +@debounce quick 0 + +# long debouncing for long-running tasks +@debounce slow 1000 +``` +#### Explicit apps + +``` +@app MyApp begin + @out quick = 12 + @out slow = 12 + @in s = "Hello" +end + +# no debouncing for fast messaging +@debounce MyApp quick 0 + +# long debouncing for long-running tasks +@debounce MyApp slow 1000 +``` +""" +macro debounce(M, fieldname, ms) + fieldname = _prepare(fieldname) + :(Stipple.debounce($M, $fieldname, $ms)) |> esc +end + +macro debounce(fieldname, ms) + fieldname = _prepare(fieldname) + :(Stipple.debounce(Stipple.@type(),$fieldname, $ms)) |> esc +end + +""" + @clear_debounce + + @clear_debounce fieldname + + @clear_debounce App + + @clear_debounce App fieldname + +Clear field-specific debounce time, for setting see `@debounce`. +After calling `@clear debounce` the field will be debounced by the value given in the +`@init` macro. + + +### Example +#### Implicit apps +``` +@app begin + @out quick = 12 + @out slow = 12 + @in s = "Hello" +end + +# no debouncing for fast messaging +@debounce quick 0 +@debounce slow 1000 + +# reset to standard value of the app +@clear_debounce quick + +# clear all field-specific debounce times +@clear_debounce +``` +#### Explicit apps + +``` +@app MyApp begin + @out quick = 12 + @out slow = 12 + @in s = "Hello" +end + +# no debouncing for fast messaging +@debounce MyApp quick 0 + +@clear_debounce MyApp quick + +# clear all field-specific debounce times +@clear_debounce MyApp +``` +""" +macro clear_debounce(M, fieldname) + fieldname = _prepare(fieldname) + :(Stipple.debounce($M, $fieldname, nothing)) |> esc +end + +macro clear_debounce(expr) + quote + if $expr isa DataType && $expr <: Stipple.ReactiveModel + Stipple.debounce($expr, nothing) + else + Stipple.debounce(Stipple.@type(), $(_prepare(expr)), nothing) + end + end |> esc +end + +macro clear_debounce() + :(Stipple.debounce(Stipple.@type(), nothing)) |> esc +end + function update_storage(m::Module) clear_type(m) # isempty(Stipple.Pages._pages) && return diff --git a/src/Stipple.jl b/src/Stipple.jl index 18a068b9..47a13832 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -115,6 +115,40 @@ const PURGE_TIME_LIMIT = Ref{Period}(Day(1)) const PURGE_NUMBER_LIMIT = Ref(1000) const PURGE_CHECK_DELAY = Ref(60) +const DEBOUNCE = LittleDict{Type{<:ReactiveModel}, LittleDict{Symbol, Any}}() + +""" + debounce(M::Type{<:ReactiveModel}, fieldnames::Union{Symbol, Vector{Symbol}}, debounce::Union{Int, Nothing} = nothing) + +Add field-specific debounce times. +""" +function debounce(M::Type{<:ReactiveModel}, fieldnames::Union{Symbol, Vector{Symbol}, NTuple{N, Symbol} where N}, debounce::Union{Int, Nothing} = nothing) + if debounce === nothing + haskey(DEBOUNCE, M) || return + d = DEBOUNCE[M] + if fieldnames isa Symbol + delete!(d, fieldnames) + else + for v in fieldnames + delete!(d, v) + end + end + isempty(d) && delete!(DEBOUNCE, M) + else + d = get!(LittleDict{Symbol, Any}, DEBOUNCE, M) + if fieldnames isa Symbol + d[fieldnames] = debounce + else + for v in fieldnames + d[v] = debounce + end + end + end + return +end + +debounce(M::Type{<:ReactiveModel}, ::Nothing) = delete!(DEBOUNCE, M) + """ `function sorted_channels()` @@ -302,20 +336,29 @@ function watch(vue_app_name::String, fieldname::Symbol, channel::String, debounc isempty(jsfunction) && (jsfunction = "Genie.WebChannels.sendMessageTo($js_channel, 'watchers', {'payload': {'field':'$fieldname', 'newval': newVal, 'oldval': oldVal, 'sesstoken': document.querySelector(\"meta[name='sesstoken']\")?.getAttribute('content')}});") + output = IOBuffer() if fieldname == :isready - output = """ + print(output, """ $vue_app_name.\$watch(function(){return this.$fieldname}, function(newVal, oldVal){$jsfunction}, {deep: true}); - """ + """) else - output = """ - $vue_app_name.\$watch(function(){return this.$fieldname}, _.debounce(function(newVal, oldVal){$jsfunction}, $debounce), {deep: true}); - """ + AM = get_abstract_type(M) + debounce = get(get(DEBOUNCE, AM, Dict{Symbol, Any}()), fieldname, debounce) + print(output, debounce == 0 ? + """ + $vue_app_name.\$watch(function(){return this.$fieldname}, function(newVal, oldVal){$jsfunction}, {deep: true}); + """ : + """ + $vue_app_name.\$watch(function(){return this.$fieldname}, _.debounce(function(newVal, oldVal){$jsfunction}, $debounce), {deep: true}); + """ + ) end # in production mode vue does not fill `this.expression` in the watcher, so we do it manually Genie.Configuration.isprod() && - (output *= "$vue_app_name._watchers[$vue_app_name._watchers.length - 1].expression = 'function(){return this.$fieldname}'") + print(output, "$vue_app_name._watchers[$vue_app_name._watchers.length - 1].expression = 'function(){return this.$fieldname}'") - output *= "\n\n" + print(output, "\n\n") + String(take!(output)) end #===# diff --git a/test/runtests.jl b/test/runtests.jl index a9a20fae..0eedd2cb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -118,6 +118,13 @@ end # check reactivity @eval model.i[] = 20 @test model.s[] == "20" + + @eval @debounce TestApp i 101 + @eval @debounce TestApp (a, b, c) 101 + @test Stipple.DEBOUNCE[TestApp][:i] == 101 + + @eval @clear_debounce TestApp + @test haskey(Stipple.DEBOUNCE, TestApp) == false end @testset "Reactive API (implicit)" begin @@ -142,6 +149,14 @@ end # check reactivity @eval model.i2[] = 20 @test model.s2[] == "20" + + # check field-specific debouncing + @eval @debounce i3 101 + @eval @debounce (a, b, c) 101 + @test Stipple.DEBOUNCE[Stipple.@type()][:i3] == 101 + + @eval @clear_debounce + @test haskey(Stipple.DEBOUNCE, Stipple.@type()) == false end @testset "Reactive API (implicit) with mixins and handlers" begin From f869dc1ec0321be653f7be1e8096595d9ba5e18e Mon Sep 17 00:00:00 2001 From: hhaensel Date: Thu, 25 Jan 2024 22:51:43 +0100 Subject: [PATCH 157/265] add docs for tuples of fieldnames --- src/ReactiveTools.jl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ReactiveTools.jl b/src/ReactiveTools.jl index bee7ffb9..6d4af598 100644 --- a/src/ReactiveTools.jl +++ b/src/ReactiveTools.jl @@ -290,7 +290,12 @@ end @debounce App fieldname ms -Set field-specific debounce time in ms +Set field-specific debounce time in ms. +### Parameters + +- `APP`: a subtype of ReactiveModel, e.g. `MyApp` +- `fieldname`: fieldname òr fieldnames as written in the declaration, e.g. `x`, `(x, y, z)` +- `ms`: debounce time in ms ### Example #### Implicit apps @@ -305,7 +310,7 @@ end @debounce quick 0 # long debouncing for long-running tasks -@debounce slow 1000 +@debounce (slow1, slow2) 1000 ``` #### Explicit apps From cfc0c7c7b8d5457739cec13304ff949e17469e52 Mon Sep 17 00:00:00 2001 From: hhaensel Date: Fri, 26 Jan 2024 08:06:01 +0100 Subject: [PATCH 158/265] remove changing of the standard object printing --- src/stipple/print.jl | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/stipple/print.jl b/src/stipple/print.jl index eafccfa2..ffc4ba0d 100644 --- a/src/stipple/print.jl +++ b/src/stipple/print.jl @@ -1,15 +1,5 @@ -function print_object(io, obj::T, omit = nothing, compact = false) where T - # currently no different printing for compact = true ... - fields = [p for p in propertynames(obj)] - omit !== nothing && setdiff!(fields, omit) - - println(io, match(r"^#*([^!]+)", String(T.name.name)).captures[1]) - for field in fields - println(io, " $field: ", Observables.to_value(getproperty(obj, field))) - end -end - function print_object(io, obj::T, omit = nothing, compact = false) where T <: ReactiveModel + # currently no different printing for compact = true ... fields = [p for p in propertynames(obj)] omit !== nothing && setdiff!(fields, omit) internal_or_auto = true From f02e3a9ea5a07d7c24684175bb614787e24e936b Mon Sep 17 00:00:00 2001 From: hhaensel Date: Fri, 26 Jan 2024 08:07:32 +0100 Subject: [PATCH 159/265] set version v0.27.35 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c1576fc8..1c123ca9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.34" +version = "0.27.35" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From b220590ac969b887822663424bb718fa22adcf72 Mon Sep 17 00:00:00 2001 From: Adrian Salceanu Date: Wed, 31 Jan 2024 20:06:33 +0100 Subject: [PATCH 160/265] Updated theme and theme API --- Project.toml | 2 +- assets/css/stipplecore.css | 234 ++++++++++++++++++++----------------- src/Elements.jl | 3 +- src/Layout.jl | 80 ++++++++++--- test/runtests.jl | 24 ++-- 5 files changed, 200 insertions(+), 143 deletions(-) diff --git a/Project.toml b/Project.toml index c1576fc8..ae949d6d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.27.34" +version = "0.28.00" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/assets/css/stipplecore.css b/assets/css/stipplecore.css index fa9b31a4..76d69eb3 100644 --- a/assets/css/stipplecore.css +++ b/assets/css/stipplecore.css @@ -1,22 +1,22 @@ -:root.stipple-blue { - --q-color-primary: #0779e4; - --q-color-secondary: #6cb1f2; +:root.stipple-core { + --q-color-primary: #1b1f3b; + --q-color-secondary: #8ea8c3; + --q-color-accent: #bea7e5; --q-color-dark: #022a50; --q-color-info: #373b44; - --q-color-positive: #72c8a9; - --q-color-negative: #bd5631; - --q-color-accent: #9c27b0; - --q-color-warning: #f2c037; - --q-color-white: #fefefe; + --q-color-white: #ffffff; --q-color-black: #302f2f; --st-text-1: #302f2f; --st-text-2: #575454; --st-text-3: #5a6460; - --st-dashboard-module: #fefefe; + --st-dashboard-module: #fcfcfc; --st-dashboard-line: #e2e2e2; - --st-dashboard-bg: #f1f3f7; + --st-dashboard-bg: #fff; --st-slider--track: #cad2d2; --st-skeleton: #e1e5ee; + --q-color-positive: #72c8a9; + --q-color-negative: #c10015; + --q-color-warning: #f2c037; } .stipple-blue .q-field--standout.q-field--focused .q-field__control { background: var(--q-color-primary); @@ -34,15 +34,14 @@ background: var(--q-color-primary); color: var(--q-color--white); } -.stipple-core .st-header, -.stipple-core .st-module { - box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.05); - overflow: auto; -} .stipple-core .st-module { - background: var(--st-dashboard-module); + border: 1px solid rgba(0, 0, 0, 0.10); + /* overflow: auto; */ + background: var(--q-color-white); border-radius: 5px; + box-shadow: 0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1); } +.container { margin-bottom: 20px; } .stipple-core .st-module > h1, .stipple-core .st-module > h2, .stipple-core .st-module > h3, @@ -50,16 +49,13 @@ .stipple-core .st-module > h5, .stipple-core .st-module > h6 { margin-top: 0; - border-left-color: var(--q-color-primary); - border-bottom: 2px solid; - border-bottom-color: var(--q-color-primary); - border-right-color: var(--q-color-primary); - border-top-color: var(--q-color-primary); display: inline-block; padding: 5px 0; } -.stipple-core body { - font-family: sans-serif; +.stipple-core, .stipple-core body { + font-weight: 400; + font-size: 90%; + background-color: var(--st-dashboard-bg); } .stipple-core { color: var(--q-color-primary); @@ -80,58 +76,36 @@ margin: 1rem 0; } .stipple-core .text-bold, -.stipple-core .text-h1, -.stipple-core .text-h2, -.stipple-core .text-h3, -.stipple-core .text-h4, -.stipple-core .text-h5, -.stipple-core .text-h6, -.stipple-core .text-subtitle1, -.stipple-core .text-subtitle2, .stipple-core b, -.stipple-core h1, -.stipple-core h2, -.stipple-core h3, -.stipple-core h4, -.stipple-core h5, -.stipple-core h6, .stipple-core strong { - font-family: sans-serif; font-weight: 700; } .stipple-core em:not(.material-icons), .stipple-core i:not(.material-icons) { - font-family: sans-serif; font-style: italic; } .stipple-core .text-h1, .stipple-core h1 { - font-size: 2rem; line-height: 2.5rem; } .stipple-core .text-h2, .stipple-core h2 { - font-size: 1.5rem; line-height: 2rem; } .stipple-core .text-h3, .stipple-core h3 { - font-size: 1.3rem; line-height: 1.7rem; } .stipple-core .text-h4, .stipple-core h4 { - font-size: 1.1rem; line-height: 1.5rem; } .stipple-core .text-h5, .stipple-core h5 { - font-size: 1rem; line-height: 1.5rem; } .stipple-core .text-h6, .stipple-core h6 { - font-size: 0.9rem; line-height: 1.3rem; } .stipple-core { @@ -140,26 +114,119 @@ } .stipple-core .container > .row, .stipple-core .st-dashboard > .row { - padding: 20px 20px 0; + padding: 2vh 2vw 0; } +.st-col { box-sizing: border-box !important; } .stipple-core .container > .row > .st-col, .stipple-core .st-dashboard > .row > .st-col { - margin-right: 20px; - padding: 20px; + margin-right: 2vw; + padding: 2vw; +} + +.st-col.col-1, +.st-col.col-xl-1, +.st-col.col-lg-1, +.st-col.col-md-1, +.st-col.col-sm-1, +.st-col.col-xs-1 { + width: calc(8.3333% - 2vw); +} +.st-col.col-2, +.st-col.col-xl-2, +.st-col.col-lg-2, +.st-col.col-md-2, +.st-col.col-sm-2, +.st-col.col-xs-2 { + width: calc(16.6667% - 2vw); +} +.st-col.col-3, +.st-col.col-xl-3, +.st-col.col-lg-3, +.st-col.col-md-3, +.st-col.col-sm-3, +.st-col.col-xs-3 { + width: calc(25% - 2vw); +} +.st-col.col-4, +.st-col.col-xl-4, +.st-col.col-lg-4, +.st-col.col-md-4, +.st-col.col-sm-4, +.st-col.col-xs-4 { + width: calc(33.3333% - 2vw); +} +.st-col.col-5, +.st-col.col-xl-5, +.st-col.col-lg-5, +.st-col.col-md-5, +.st-col.col-sm-5, +.st-col.col-xs-5 { + width: calc(41.6667% - 2vw); +} +.st-col.col-6, +.st-col.col-xl-6, +.st-col.col-lg-6, +.st-col.col-md-6, +.st-col.col-sm-6, +.st-col.col-xs-6 { + width: calc(50% - 2vw); +} +.st-col.col-7, +.st-col.col-lx-7, +.st-col.col-lg-7, +.st-col.col-md-7, +.st-col.col-sm-7, +.st-col.col-xs-7 { + width: calc(58.3333% - 2vw); +} +.st-col.col-8, +.st-col.col-xl-8, +.st-col.col-lg-8, +.st-col.col-md-8, +.st-col.col-sm-8, +.st-col.col-xs-8 { + width: calc(66.6666% - 2vw); +} +.st-col.col-9, +.st-col.col-xl-9, +.st-col.col-lg-9, +.st-col.col-md-9, +.st-col.col-sm-9, +.st-col.col-xs-9 { + width: calc(75% - 2vw); +} +.st-col.col-10, +.st-col.col-xl-10, +.st-col.col-lg-10, +.st-col.col-md-10, +.st-col.col-sm-10, +.st-col.col-xs-10 { + width: calc(83.3333% - 2vw); +} +.st-col.col-11, +.st-col.col-xs-11, +.st-col.col-lg-11, +.st-col.col-md-11, +.st-col.col-sm-11, +.st-col.col-xs-11 { + width: calc(91.6666% - 2vw); +} +.st-col.col-12, +.st-col.col-xl-12, +.st-col.col-lg-12, +.st-col.col-md-12, +.st-col.col-sm-12, +.st-col.col-xs-12 { + width: calc(100% - 2vw); } + .stipple-core .container > .row > .st-col:last-child, .stipple-core .st-dashboard > .row > .st-col:last-child { margin-right: 0; } -@media (max-width: 599px) { - .stipple-core .container > .row > .st-col:not(:first-child), - .stipple-core .st-dashboard > .row > .st-col:not(:first-child) { - margin-top: 20px; - } -} .stipple-core .container > .row > .st-col > .row:not(:first-child), .stipple-core .st-dashboard > .row > .st-col > .row:not(:first-child) { - margin-top: 1rem; + margin-top: 2vh; } .stipple-core .container > .row.row--stack, .stipple-core .st-dashboard > .row.row--stack { @@ -172,51 +239,17 @@ margin-top: -5px; border-top: 1px solid var(--st-dashboard-line); } -.st-bt { - border-top: 1px solid var(--st-dashboard-line); -} -.st-bb { - border-bottom: 1px solid var(--st-dashboard-line); -} -.st-br { - border-right: 1px solid var(--st-dashboard-line); -} -.st-br.st-col:last-child { - border-right: 0; -} -@media (max-width: 599px) { - .st-br.st-col { - border-right: 0; - } -} -.st-bl { - border-left: 1px solid var(--st-dashboard-line); -} -.st-bl.st-col:first-child { - border-left: 0; -} -@media (max-width: 599px) { - .st-bl.st-col { - border-left: 0; - } -} .stipple-core fieldset { border: none; } .stipple-core .q-field--float.q-field--focused.q-field--standout .q-field__control { - font-family: sans-serif; font-weight: 700; } .stipple-core .q-input .q-field__native, .stipple-core .q-select .q-field__native { - font-family: sans-serif; font-weight: 700; font-size: 1.1em; } -.stipple-core .q-input .q-field__native::placeholder, -.stipple-core .q-select .q-field__native::placeholder { - font-family: sans-serif; -} .stipple-core .q-field--rounded.q-field--filled .q-field__control { border-radius: 5px 5px 0 0; } @@ -233,7 +266,6 @@ color: var(--q-color-white); } .stipple-core .q-badge { - font-family: sans-serif; font-weight: 700; padding: 4px 6px; } @@ -243,7 +275,6 @@ } .stipple-core .q-banner { border-radius: 5px; - font-family: sans-serif; font-weight: 700; } .q-ripple { @@ -264,7 +295,6 @@ box-shadow: none; } .stipple-core .q-btn { - font-family: sans-serif; font-weight: 700; } .stipple-core .q-btn-group { @@ -285,7 +315,6 @@ margin: 0; } .stipple-core .q-chip { - font-family: sans-serif; font-weight: 700; } .stipple-core .q-file .q-field__marginal { @@ -299,7 +328,6 @@ body > .q-loading-bar { font-size: 15px; } .stipple-core .q-item.q-item--active { - font-family: sans-serif; font-weight: 700; } .stipple-core .q-item.q-manual-focusable--focused > .q-focus-helper { @@ -325,7 +353,6 @@ body > .q-loading-bar { min-height: 20px; } .stipple-core .q-slider__pin-text { - font-family: sans-serif; font-weight: 700; } /* .stipple-core .q-slider__track-container { @@ -336,7 +363,6 @@ body > .q-loading-bar { } .stipple-core .q-table th { color: var(--st-text-1); - font-family: sans-serif; font-weight: 900; font-size: 0.8rem; } @@ -349,7 +375,6 @@ body > .q-loading-bar { padding: 0 5px; } .stipple-core .q-table__title { - font-family: sans-serif; font-weight: 700; font-size: 14px; } @@ -416,8 +441,8 @@ body > .q-loading-bar { padding: 1rem 0; } .stipple-core .st-big-number .q-badge { - font-family: sans-serif; font-weight: 900; + font-size: large; margin: 0.5rem; padding: 8px 15px; display: block; @@ -425,7 +450,6 @@ body > .q-loading-bar { color: var(--st-text-1); } .stipple-core .st-big-number__num { - font-family: sans-serif; font-weight: 700; display: block; color: var(--st-text-1); @@ -472,18 +496,8 @@ body > .q-loading-bar { height: 17px; margin-right: 2px; } -@media (max-width: 599px) { - .stipple-core .st-footer { - padding-top: 1rem; - } - .stipple-core .st-footer .st-logo { - margin: auto; - display: inline-block; - } -} .stipple-core .st-header { - border-bottom: 1px solid var(--st-dashboard-line); - background: var(--st-dashboard-module); + background: var(--q-color-white); padding-left: 20px; } @media (max-width: 599px) { diff --git a/src/Elements.jl b/src/Elements.jl index b94a23b0..4ba36d78 100644 --- a/src/Elements.jl +++ b/src/Elements.jl @@ -54,7 +54,6 @@ function vue_integration(::Type{M}; 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), "\"{" => " {") @@ -66,7 +65,7 @@ function vue_integration(::Type{M}; " function initStipple(rootSelector){ - Stipple.init($( core_theme ? "{theme: 'stipple-blue'}" : "" )); + 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 diff --git a/src/Layout.jl b/src/Layout.jl index 85d3ee07..96088c09 100644 --- a/src/Layout.jl +++ b/src/Layout.jl @@ -11,7 +11,7 @@ export layout, add_css, remove_css export page, app, row, column, cell, container, flexgrid_kwargs, htmldiv export theme -const THEMES = Function[] +const THEMES = Ref(Function[]) """ function layout(output::Union{String,Vector}; partial::Bool = false, title::String = "", class::String = "", style::String = "", @@ -85,7 +85,8 @@ function page(model::M, args...; partial::Bool = false, title::String = "", class::String = "container", style::String = "", channel::String = Genie.config.webchannels_default_route, head_content::Union{AbstractString, Vector{<:AbstractString}} = "", prepend::Union{S,Vector} = "", append::Union{T,Vector} = [], - core_theme::Bool = true, kwargs...)::ParsedHTMLString where {M<:Stipple.ReactiveModel, S<:AbstractString,T<:AbstractString} + core_theme::Bool = true, + kwargs...)::ParsedHTMLString where {M<:Stipple.ReactiveModel, S<:AbstractString,T<:AbstractString} layout( [ @@ -149,7 +150,7 @@ function flexgrid_kwargs(; class = "", class! = nothing, symbol_class::Bool = tr join(pushfirst!(classes, class), ' ') end end - + (class isa Symbol || class isa String && length(class) > 0) && (kwargs[:class] = class) if ! symbol_class && class isa Symbol || class isa Vector || class isa AbstractDict @@ -181,7 +182,7 @@ end function row(args...; size=-1, xs=-1, sm=-1, md=-1, lg=-1, xl=-1, kwargs...) Creates a `div` HTML element with Quasar's Flexgrid CSS class `row`. Such rows typically contain elements created with -[`cell`](@ref), `row`, [`column`](@ref) or other elements that manually receive grid classes, e.g. `"col"`, `"col-sm-5"`. +[`cell`](@ref), `row`, [`column`](@ref) or other elements that manually receive grid classes, e.g. `"col"`, `"col-sm-5"`. The grid size kwargs `size`, `xs`, etc. are explained in more detail in the docs of [`cell`](@ref). ### Example @@ -191,7 +192,7 @@ julia> row(span("Hello")) "
Hello
" ``` """ -function row(args...; +function row(args...; col::Union{Int,AbstractString,Symbol,Nothing} = -1, xs::Union{Int,AbstractString,Symbol,Nothing} = -1, sm::Union{Int,AbstractString,Symbol,Nothing} = -1, md::Union{Int,AbstractString,Symbol,Nothing} = -1, lg::Union{Int,AbstractString,Symbol,Nothing} = -1, xl::Union{Int,AbstractString,Symbol,Nothing} = -1, size::Union{Int,AbstractString,Symbol,Nothing} = -1, @@ -211,7 +212,7 @@ end function column(args...; size=-1, xs=-1, sm=-1, md=-1, lg=-1, xl=-1, kwargs...) Creates a `div` HTML element with Quasar's Flexgrid CSS class `column`. Such columns typically contain elements created with -[`cell`](@ref), [`row`](@ref), `column`, or other elements that manually receive grid classes, e.g. `"col"`, `"col-sm-5"`. +[`cell`](@ref), [`row`](@ref), `column`, or other elements that manually receive grid classes, e.g. `"col"`, `"col-sm-5"`. The grid size kwargs `size`, `xs`, etc. are explained in more detail in the docs of [`cell`](@ref). @@ -272,7 +273,7 @@ function cell(args...; ) # for backward compatibility with `size` kwarg col == 0 && size != 0 && (col = size) - + # class = class isa Symbol ? Symbol("$class + ' st-col'") : join(push!(split(class), "st-col"), " ") class = append_class(class, "st-col") kwargs = Stipple.attributes(flexgrid_kwargs(; class, col, xs, sm, md, lg, xl, symbol_class = false, kwargs...)) @@ -311,7 +312,7 @@ end function theme() :: String Provides theming support for Stipple apps and pages. It includes Stipple's default CSS files and additional elements, - in the form of HTML tags, can be injected by pushing to the `Stipple.Layout.THEMES` collection. + in the form of HTML tags, can be injected by pushing to the `Stipple.Layout.THEMES[][]` collection. ### Example @@ -322,20 +323,23 @@ julia> theme() julia> StippleUI.theme() "" -julia> push!(Stipple.Layout.THEMES, StippleUI.theme) +julia> push!(Stipple.Layout.THEMES[], StippleUI.theme) ``` """ function theme(; core_theme::Bool = true) :: Vector{String} output = String[] if core_theme - push!(output, - stylesheet("https://fonts.googleapis.com/css?family=Material+Icons"), - stylesheet(Genie.Assets.asset_path(Stipple.assets_config, :css, file="stipplecore")) + push!(THEMES[], () -> begin + stylesheet("https://fonts.googleapis.com/css?family=Material+Icons"), + stylesheet(Genie.Assets.asset_path(Stipple.assets_config, :css, file="stipplecore")) + end ) end - for f in THEMES + unique!(THEMES[]) + + for f in THEMES[] push!(output, f()...) end @@ -345,7 +349,7 @@ end """ add_css(css::Function; update = true) -Add a css function to the `THEMES`. +Add a css function to the `THEMES[]`. ### Params * `css::Function` - a function that results in a vector of style elements @@ -371,18 +375,58 @@ add_css(mycss) function add_css(css::Function; update = true) # removal can only be done by name, as the old function has already been overwritten update && remove_css(css::Function, byname = true) - push!(Stipple.Layout.THEMES, css) + push!(THEMES[], css) + unique!(THEMES[]) +end + +""" + add_css(css...; update = false) + +Adds a css file to the layout. + +### Example +```julia +Stipple.Layout.add_css("froop.css", "bootstrap.css", "https://cdn.tailwindcss.com/tailwind.css") +``` +""" +function add_css(css...; update = false) + css = [Stipple.Elements.stylesheet(c) for c in css] + add_css(() -> css; update = update) +end + +""" + add_script(script...; update = false) + +Adds a script file to the layout. + +### Example +```julia +Stipple.Layout.add_script("froop.js", "bootstrap.js", "https://cdn.tailwindcss.com/tailwind.js") +``` +""" +function add_script(script...; update = false) + script = [Stipple.Elements.script(src=s) for s in script] + add_css(() -> script; update = update) end """ remove_css(css::Function, byname::Bool = false) -Remove a stylesheet function from the stack (`Stipple.Layout.THEMES`) +Remove a stylesheet function from the stack (`THEMES[]`) If called with `byname = true`, the function will be identified by name rather than by the function itself. """ function remove_css(css::Function; byname::Bool = false) - inds = byname ? nameof.(Stipple.Layout.THEMES) .== nameof(css) : Stipple.Layout.THEMES .== css - deleteat!(Stipple.Layout.THEMES, inds) + inds = byname ? nameof.(THEMES[]) .== nameof(css) : THEMES[] .== css + deleteat!(THEMES[], inds) +end + +""" + theme!(css::Function) + +Replace the current themes with new ones. +""" +function theme!(css::Function) + THEMES[] = [css] end end diff --git a/test/runtests.jl b/test/runtests.jl index a9a20fae..9440389c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -42,7 +42,7 @@ end # channels have to be different @test model.channel__ != model2.channel__ - + # check whether fields are correctly defined @test propertynames(model) == tuple(Stipple.INTERNALFIELDS..., Stipple.AUTOFIELDS..., :i, :s) @@ -132,7 +132,7 @@ end @eval model = @init @eval model2 = @init - + # channels have to be different @eval @test model.channel__ != model2.channel__ @@ -244,7 +244,7 @@ end s1 = string_get("http://localhost:$port/") s2 = string_get("http://localhost:$port/") s3 = string_get("http://localhost:$port/", cookies = false) - + s4 = string_get("http://localhost:$port/static") s5 = string_get("http://localhost:$port/static") s6 = string_get("http://localhost:$port/static", cookies = false) @@ -304,7 +304,7 @@ end s1 = string_get("http://localhost:$port/") s2 = string_get("http://localhost:$port/") s3 = string_get("http://localhost:$port/", cookies = false) - + s4 = string_get("http://localhost:$port/static") s5 = string_get("http://localhost:$port/static") s6 = string_get("http://localhost:$port/static", cookies = false) @@ -312,7 +312,7 @@ end @test get_channel(s2) == get_channel(s1) @test get_channel(s3) != get_channel(s1) @test get_channel(s4) == get_channel(s5) == get_channel(s6) - + @clear_cache MyApp down() end @@ -340,7 +340,7 @@ end el = row(col = 2, sm = 9, class = :myclass) @test contains(el, ":class=\"[myclass,'row','col-2','col-sm-9']\"") - + el = row(col = 2, sm = 9, class! = "myclass") @test contains(el, ":class=\"[myclass,'row','col-2','col-sm-9']\"") @@ -481,15 +481,15 @@ end end add_css(my_css) - @test Stipple.Layout.THEMES[end] == my_css + @test Stipple.Layout.THEMES[][end] == my_css - n = length(Stipple.Layout.THEMES) + n = length(Stipple.Layout.THEMES[]) remove_css(my_css) - @test length(Stipple.Layout.THEMES) == n - 1 - @test findfirst(==(my_css), Stipple.Layout.THEMES) === nothing + @test length(Stipple.Layout.THEMES[]) == n - 1 + @test findfirst(==(my_css), Stipple.Layout.THEMES[]) === nothing add_css(my_css) - @test Stipple.Layout.THEMES[end] == my_css + @test Stipple.Layout.THEMES[][end] == my_css remove_css(my_css, byname = true) - @test findfirst(==(my_css), Stipple.Layout.THEMES) === nothing + @test findfirst(==(my_css), Stipple.Layout.THEMES[]) === nothing end \ No newline at end of file From 852ac7faacef1334fe23583ecc0d47ce407ce670 Mon Sep 17 00:00:00 2001 From: hhaensel <31985040+hhaensel@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:23:03 +0100 Subject: [PATCH 161/265] fix diverging models in watcher and event channels (#260) --- src/Stipple.jl | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/Stipple.jl b/src/Stipple.jl index 2a572f84..67c28b4e 100644 --- a/src/Stipple.jl +++ b/src/Stipple.jl @@ -619,27 +619,25 @@ function init(t::Type{M}; end ch = "/$channel/events" - if ! Genie.Router.ischannel(Router.channelname(ch)) - Genie.Router.channel(ch, named = Router.channelname(ch)) do - # get event name - event = Genie.Requests.payload(:payload)["event"] - # form handler parameter & call event notifier - handler = Symbol(get(event, "name", nothing)) - event_info = get(event, "event", nothing) - - # add client id if requested - if event_info isa Dict && get(event_info, "_addclient", false) - client = transport == Genie.WebChannels ? Genie.WebChannels.id(Genie.Requests.wsclient()) : Genie.Requests.wtclient() - push!(event_info, "_client" => client) - end + Genie.Router.channel(ch, named = Router.channelname(ch)) do + # get event name + event = Genie.Requests.payload(:payload)["event"] + # form handler parameter & call event notifier + handler = Symbol(get(event, "name", nothing)) + event_info = get(event, "event", nothing) + + # add client id if requested + if event_info isa Dict && get(event_info, "_addclient", false) + client = transport == Genie.WebChannels ? Genie.WebChannels.id(Genie.Requests.wsclient()) : Genie.Requests.wtclient() + push!(event_info, "_client" => client) + end - isempty(methods(notify, (M, Val{handler}))) || notify(model, Val(handler)) - isempty(methods(notify, (M, Val{handler}, Any))) || notify(model, Val(handler), event_info) + isempty(methods(notify, (M, Val{handler}))) || notify(model, Val(handler)) + isempty(methods(notify, (M, Val{handler}, Any))) || notify(model, Val(handler), event_info) - LAST_ACTIVITY[Symbol(channel)] = now() + LAST_ACTIVITY[Symbol(channel)] = now() - ok_response - end + ok_response end end From 08c8ec8c8e0f51cf3c0eb744b5e0d3ec48368252 Mon Sep 17 00:00:00 2001 From: hhaensel <31985040+hhaensel@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:28:16 +0100 Subject: [PATCH 162/265] set version v0.28.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index ae949d6d..f3aa6000 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.28.00" +version = "0.28.1" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From ccdb80aa82d6c49956e4fe43a3ca53bb591d0264 Mon Sep 17 00:00:00 2001 From: hhaensel <31985040+hhaensel@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:46:45 +0100 Subject: [PATCH 163/265] fix parse_jsfunction() (#262) --- src/stipple/jsintegration.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/stipple/jsintegration.jl b/src/stipple/jsintegration.jl index ae3559d8..a411afe8 100644 --- a/src/stipple/jsintegration.jl +++ b/src/stipple/jsintegration.jl @@ -5,14 +5,14 @@ Checks whether the string is a valid js function and returns a `Dict` from which in the backend can construct a function. """ function parse_jsfunction(s::AbstractString) - # look for classical function definition - m = match( r"function\s*\(([^)]*)\)\s*{(.*)}"s, s) + # look for classical function definition (not a full syntax check, though) + m = match( r"^\s*function\s*\(([^)]*)\)\s*{(.*)}\s*$"s, s) !isnothing(m) && length(m.captures) == 2 && return opts(arguments=m[1], body=m[2]) - - # look for pure function definition - m = match( r"\s*\(?([^=)]*?)\)?\s*=>\s*({*.*?}*)\s*$"s , s ) + + # look for pure function definition including unbracketed single parameter + m = match( r"^\s*\(?([^=<>:;.(){}\[\]]*?)\)?\s*=>\s*({*.*?}*)\s*$"s , s ) (isnothing(m) || length(m.captures) != 2) && return nothing - + # if pure function body is without curly brackets, add a `return`, otherwise strip the brackets # Note: for utf-8 strings m[2][2:end-1] will fail if the string ends with a wide character, e.g. ϕ body = startswith(m[2], "{") ? m[2][2:prevind(m[2], lastindex(m[2]))] : "return " * m[2] From dbb1f5c9a54977faa1a5d9b5ebb5b99dbf94f694 Mon Sep 17 00:00:00 2001 From: hhaensel <31985040+hhaensel@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:48:49 +0100 Subject: [PATCH 164/265] set version v0.28.2 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f3aa6000..82f21e85 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Stipple" uuid = "4acbeb90-81a0-11ea-1966-bdaff8155998" authors = ["Adrian "] -version = "0.28.1" +version = "0.28.2" [deps] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" From c72238ab57797e7535c3b24a9a527c1d0cdab9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helmut=20H=C3=A4nsel?= Date: Sun, 11 Feb 2024 01:40:54 +0100 Subject: [PATCH 165/265] switch to Vue 3 --- assets/js/stipplecore.js | 4 +- assets/js/vue.global.js | 16647 +++++++++++++++++++++++++++++++++ assets/js/vue.global.prod.js | 11 + assets/js/vue_filters.js | 8 +- assets/js/watchers.js | 25 +- src/Elements.jl | 4 +- src/Layout.jl | 6 +- src/Stipple.jl | 4 +- src/stipple/rendering.jl | 7 +- 9 files changed, 16684 insertions(+), 32 deletions(-) create mode 100644 assets/js/vue.global.js create mode 100644 assets/js/vue.global.prod.js diff --git a/assets/js/stipplecore.js b/assets/js/stipplecore.js index af23d5de..a4a8608c 100644 --- a/assets/js/stipplecore.js +++ b/assets/js/stipplecore.js @@ -136,8 +136,8 @@ var t; if (!Vue) throw "Stipple requires Vue"; e = Object.assign({}, e); - Vue.component("StDashboard", c); - Vue.component("StBigNumber", a); + // Vue.component("StDashboard", c); + // Vue.component("StBigNumber", a); (t = document.querySelector("html").classList).add.apply(t, ["stipple-core", e.theme ? e.theme : "stipple-blue"]); }, }; diff --git a/assets/js/vue.global.js b/assets/js/vue.global.js new file mode 100644 index 00000000..5754f319 --- /dev/null +++ b/assets/js/vue.global.js @@ -0,0 +1,16647 @@ +/** +* vue v3.4.18 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/ +var Vue = (function (exports) { + 'use strict'; + + function makeMap(str, expectsLowerCase) { + const set = new Set(str.split(",")); + return expectsLowerCase ? (val) => set.has(val.toLowerCase()) : (val) => set.has(val); + } + + const EMPTY_OBJ = Object.freeze({}) ; + const EMPTY_ARR = Object.freeze([]) ; + const NOOP = () => { + }; + const NO = () => false; + const isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // uppercase letter + (key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); + const isModelListener = (key) => key.startsWith("onUpdate:"); + const extend = Object.assign; + const remove = (arr, el) => { + const i = arr.indexOf(el); + if (i > -1) { + arr.splice(i, 1); + } + }; + const hasOwnProperty$1 = Object.prototype.hasOwnProperty; + const hasOwn = (val, key) => hasOwnProperty$1.call(val, key); + const isArray = Array.isArray; + const isMap = (val) => toTypeString(val) === "[object Map]"; + const isSet = (val) => toTypeString(val) === "[object Set]"; + const isDate = (val) => toTypeString(val) === "[object Date]"; + const isRegExp = (val) => toTypeString(val) === "[object RegExp]"; + const isFunction = (val) => typeof val === "function"; + const isString = (val) => typeof val === "string"; + const isSymbol = (val) => typeof val === "symbol"; + const isObject = (val) => val !== null && typeof val === "object"; + const isPromise = (val) => { + return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); + }; + const objectToString = Object.prototype.toString; + const toTypeString = (value) => objectToString.call(value); + const toRawType = (value) => { + return toTypeString(value).slice(8, -1); + }; + const isPlainObject = (val) => toTypeString(val) === "[object Object]"; + const isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; + const isReservedProp = /* @__PURE__ */ makeMap( + // the leading comma is intentional so empty string "" is also included + ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" + ); + const isBuiltInDirective = /* @__PURE__ */ makeMap( + "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" + ); + const cacheStringFunction = (fn) => { + const cache = /* @__PURE__ */ Object.create(null); + return (str) => { + const hit = cache[str]; + return hit || (cache[str] = fn(str)); + }; + }; + const camelizeRE = /-(\w)/g; + const camelize = cacheStringFunction((str) => { + return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); + }); + const hyphenateRE = /\B([A-Z])/g; + const hyphenate = cacheStringFunction( + (str) => str.replace(hyphenateRE, "-$1").toLowerCase() + ); + const capitalize = cacheStringFunction((str) => { + return str.charAt(0).toUpperCase() + str.slice(1); + }); + const toHandlerKey = cacheStringFunction((str) => { + const s = str ? `on${capitalize(str)}` : ``; + return s; + }); + const hasChanged = (value, oldValue) => !Object.is(value, oldValue); + const invokeArrayFns = (fns, arg) => { + for (let i = 0; i < fns.length; i++) { + fns[i](arg); + } + }; + const def = (obj, key, value) => { + Object.defineProperty(obj, key, { + configurable: true, + enumerable: false, + value + }); + }; + const looseToNumber = (val) => { + const n = parseFloat(val); + return isNaN(n) ? val : n; + }; + const toNumber = (val) => { + const n = isString(val) ? Number(val) : NaN; + return isNaN(n) ? val : n; + }; + let _globalThis; + const getGlobalThis = () => { + return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); + }; + + const PatchFlagNames = { + [1]: `TEXT`, + [2]: `CLASS`, + [4]: `STYLE`, + [8]: `PROPS`, + [16]: `FULL_PROPS`, + [32]: `NEED_HYDRATION`, + [64]: `STABLE_FRAGMENT`, + [128]: `KEYED_FRAGMENT`, + [256]: `UNKEYED_FRAGMENT`, + [512]: `NEED_PATCH`, + [1024]: `DYNAMIC_SLOTS`, + [2048]: `DEV_ROOT_FRAGMENT`, + [-1]: `HOISTED`, + [-2]: `BAIL` + }; + + const slotFlagsText = { + [1]: "STABLE", + [2]: "DYNAMIC", + [3]: "FORWARDED" + }; + + const GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error"; + const isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED); + + const range = 2; + function generateCodeFrame(source, start = 0, end = source.length) { + let lines = source.split(/(\r?\n)/); + const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); + lines = lines.filter((_, idx) => idx % 2 === 0); + let count = 0; + const res = []; + for (let i = 0; i < lines.length; i++) { + count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); + if (count >= start) { + for (let j = i - range; j <= i + range || end > count; j++) { + if (j < 0 || j >= lines.length) + continue; + const line = j + 1; + res.push( + `${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}` + ); + const lineLength = lines[j].length; + const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; + if (j === i) { + const pad = start - (count - (lineLength + newLineSeqLength)); + const length = Math.max( + 1, + end > count ? lineLength - pad : end - start + ); + res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); + } else if (j > i) { + if (end > count) { + const length = Math.max(Math.min(end - count, lineLength), 1); + res.push(` | ` + "^".repeat(length)); + } + count += lineLength + newLineSeqLength; + } + } + break; + } + } + return res.join("\n"); + } + + function normalizeStyle(value) { + if (isArray(value)) { + const res = {}; + for (let i = 0; i < value.length; i++) { + const item = value[i]; + const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); + if (normalized) { + for (const key in normalized) { + res[key] = normalized[key]; + } + } + } + return res; + } else if (isString(value) || isObject(value)) { + return value; + } + } + const listDelimiterRE = /;(?![^(]*\))/g; + const propertyDelimiterRE = /:([^]+)/; + const styleCommentRE = /\/\*[^]*?\*\//g; + function parseStringStyle(cssText) { + const ret = {}; + cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { + if (item) { + const tmp = item.split(propertyDelimiterRE); + tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); + } + }); + return ret; + } + function stringifyStyle(styles) { + let ret = ""; + if (!styles || isString(styles)) { + return ret; + } + for (const key in styles) { + const value = styles[key]; + const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); + if (isString(value) || typeof value === "number") { + ret += `${normalizedKey}:${value};`; + } + } + return ret; + } + function normalizeClass(value) { + let res = ""; + if (isString(value)) { + res = value; + } else if (isArray(value)) { + for (let i = 0; i < value.length; i++) { + const normalized = normalizeClass(value[i]); + if (normalized) { + res += normalized + " "; + } + } + } else if (isObject(value)) { + for (const name in value) { + if (value[name]) { + res += name + " "; + } + } + } + return res.trim(); + } + function normalizeProps(props) { + if (!props) + return null; + let { class: klass, style } = props; + if (klass && !isString(klass)) { + props.class = normalizeClass(klass); + } + if (style) { + props.style = normalizeStyle(style); + } + return props; + } + + const HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; + const SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; + const MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics"; + const VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; + const isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); + const isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); + const isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS); + const isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); + + const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; + const isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); + const isBooleanAttr = /* @__PURE__ */ makeMap( + specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected` + ); + function includeBooleanAttr(value) { + return !!value || value === ""; + } + const isKnownHtmlAttr = /* @__PURE__ */ makeMap( + `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap` + ); + const isKnownSvgAttr = /* @__PURE__ */ makeMap( + `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan` + ); + function isRenderableAttrValue(value) { + if (value == null) { + return false; + } + const type = typeof value; + return type === "string" || type === "number" || type === "boolean"; + } + + function looseCompareArrays(a, b) { + if (a.length !== b.length) + return false; + let equal = true; + for (let i = 0; equal && i < a.length; i++) { + equal = looseEqual(a[i], b[i]); + } + return equal; + } + function looseEqual(a, b) { + if (a === b) + return true; + let aValidType = isDate(a); + let bValidType = isDate(b); + if (aValidType || bValidType) { + return aValidType && bValidType ? a.getTime() === b.getTime() : false; + } + aValidType = isSymbol(a); + bValidType = isSymbol(b); + if (aValidType || bValidType) { + return a === b; + } + aValidType = isArray(a); + bValidType = isArray(b); + if (aValidType || bValidType) { + return aValidType && bValidType ? looseCompareArrays(a, b) : false; + } + aValidType = isObject(a); + bValidType = isObject(b); + if (aValidType || bValidType) { + if (!aValidType || !bValidType) { + return false; + } + const aKeysCount = Object.keys(a).length; + const bKeysCount = Object.keys(b).length; + if (aKeysCount !== bKeysCount) { + return false; + } + for (const key in a) { + const aHasKey = a.hasOwnProperty(key); + const bHasKey = b.hasOwnProperty(key); + if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key])) { + return false; + } + } + } + return String(a) === String(b); + } + function looseIndexOf(arr, val) { + return arr.findIndex((item) => looseEqual(item, val)); + } + + const toDisplayString = (val) => { + return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); + }; + const replacer = (_key, val) => { + if (val && val.__v_isRef) { + return replacer(_key, val.value); + } else if (isMap(val)) { + return { + [`Map(${val.size})`]: [...val.entries()].reduce( + (entries, [key, val2], i) => { + entries[stringifySymbol(key, i) + " =>"] = val2; + return entries; + }, + {} + ) + }; + } else if (isSet(val)) { + return { + [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v)) + }; + } else if (isSymbol(val)) { + return stringifySymbol(val); + } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { + return String(val); + } + return val; + }; + const stringifySymbol = (v, i = "") => { + var _a; + return isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v; + }; + + function warn$2(msg, ...args) { + console.warn(`[Vue warn] ${msg}`, ...args); + } + + let activeEffectScope; + class EffectScope { + constructor(detached = false) { + this.detached = detached; + /** + * @internal + */ + this._active = true; + /** + * @internal + */ + this.effects = []; + /** + * @internal + */ + this.cleanups = []; + this.parent = activeEffectScope; + if (!detached && activeEffectScope) { + this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push( + this + ) - 1; + } + } + get active() { + return this._active; + } + run(fn) { + if (this._active) { + const currentEffectScope = activeEffectScope; + try { + activeEffectScope = this; + return fn(); + } finally { + activeEffectScope = currentEffectScope; + } + } else { + warn$2(`cannot run an inactive effect scope.`); + } + } + /** + * This should only be called on non-detached scopes + * @internal + */ + on() { + activeEffectScope = this; + } + /** + * This should only be called on non-detached scopes + * @internal + */ + off() { + activeEffectScope = this.parent; + } + stop(fromParent) { + if (this._active) { + let i, l; + for (i = 0, l = this.effects.length; i < l; i++) { + this.effects[i].stop(); + } + for (i = 0, l = this.cleanups.length; i < l; i++) { + this.cleanups[i](); + } + if (this.scopes) { + for (i = 0, l = this.scopes.length; i < l; i++) { + this.scopes[i].stop(true); + } + } + if (!this.detached && this.parent && !fromParent) { + const last = this.parent.scopes.pop(); + if (last && last !== this) { + this.parent.scopes[this.index] = last; + last.index = this.index; + } + } + this.parent = void 0; + this._active = false; + } + } + } + function effectScope(detached) { + return new EffectScope(detached); + } + function recordEffectScope(effect, scope = activeEffectScope) { + if (scope && scope.active) { + scope.effects.push(effect); + } + } + function getCurrentScope() { + return activeEffectScope; + } + function onScopeDispose(fn) { + if (activeEffectScope) { + activeEffectScope.cleanups.push(fn); + } else { + warn$2( + `onScopeDispose() is called when there is no active effect scope to be associated with.` + ); + } + } + + let activeEffect; + class ReactiveEffect { + constructor(fn, trigger, scheduler, scope) { + this.fn = fn; + this.trigger = trigger; + this.scheduler = scheduler; + this.active = true; + this.deps = []; + /** + * @internal + */ + this._dirtyLevel = 4; + /** + * @internal + */ + this._trackId = 0; + /** + * @internal + */ + this._runnings = 0; + /** + * @internal + */ + this._shouldSchedule = false; + /** + * @internal + */ + this._depsLength = 0; + recordEffectScope(this, scope); + } + get dirty() { + if (this._dirtyLevel === 2 || this._dirtyLevel === 3) { + this._dirtyLevel = 1; + pauseTracking(); + for (let i = 0; i < this._depsLength; i++) { + const dep = this.deps[i]; + if (dep.computed) { + triggerComputed(dep.computed); + if (this._dirtyLevel >= 4) { + break; + } + } + } + if (this._dirtyLevel === 1) { + this._dirtyLevel = 0; + } + resetTracking(); + } + return this._dirtyLevel >= 4; + } + set dirty(v) { + this._dirtyLevel = v ? 4 : 0; + } + run() { + this._dirtyLevel = 0; + if (!this.active) { + return this.fn(); + } + let lastShouldTrack = shouldTrack; + let lastEffect = activeEffect; + try { + shouldTrack = true; + activeEffect = this; + this._runnings++; + preCleanupEffect(this); + return this.fn(); + } finally { + postCleanupEffect(this); + this._runnings--; + activeEffect = lastEffect; + shouldTrack = lastShouldTrack; + } + } + stop() { + var _a; + if (this.active) { + preCleanupEffect(this); + postCleanupEffect(this); + (_a = this.onStop) == null ? void 0 : _a.call(this); + this.active = false; + } + } + } + function triggerComputed(computed) { + return computed.value; + } + function preCleanupEffect(effect2) { + effect2._trackId++; + effect2._depsLength = 0; + } + function postCleanupEffect(effect2) { + if (effect2.deps.length > effect2._depsLength) { + for (let i = effect2._depsLength; i < effect2.deps.length; i++) { + cleanupDepEffect(effect2.deps[i], effect2); + } + effect2.deps.length = effect2._depsLength; + } + } + function cleanupDepEffect(dep, effect2) { + const trackId = dep.get(effect2); + if (trackId !== void 0 && effect2._trackId !== trackId) { + dep.delete(effect2); + if (dep.size === 0) { + dep.cleanup(); + } + } + } + function effect(fn, options) { + if (fn.effect instanceof ReactiveEffect) { + fn = fn.effect.fn; + } + const _effect = new ReactiveEffect(fn, NOOP, () => { + if (_effect.dirty) { + _effect.run(); + } + }); + if (options) { + extend(_effect, options); + if (options.scope) + recordEffectScope(_effect, options.scope); + } + if (!options || !options.lazy) { + _effect.run(); + } + const runner = _effect.run.bind(_effect); + runner.effect = _effect; + return runner; + } + function stop(runner) { + runner.effect.stop(); + } + let shouldTrack = true; + let pauseScheduleStack = 0; + const trackStack = []; + function pauseTracking() { + trackStack.push(shouldTrack); + shouldTrack = false; + } + function resetTracking() { + const last = trackStack.pop(); + shouldTrack = last === void 0 ? true : last; + } + function pauseScheduling() { + pauseScheduleStack++; + } + function resetScheduling() { + pauseScheduleStack--; + while (!pauseScheduleStack && queueEffectSchedulers.length) { + queueEffectSchedulers.shift()(); + } + } + function trackEffect(effect2, dep, debuggerEventExtraInfo) { + var _a; + if (dep.get(effect2) !== effect2._trackId) { + dep.set(effect2, effect2._trackId); + const oldDep = effect2.deps[effect2._depsLength]; + if (oldDep !== dep) { + if (oldDep) { + cleanupDepEffect(oldDep, effect2); + } + effect2.deps[effect2._depsLength++] = dep; + } else { + effect2._depsLength++; + } + { + (_a = effect2.onTrack) == null ? void 0 : _a.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo)); + } + } + } + const queueEffectSchedulers = []; + function triggerEffects(dep, dirtyLevel, debuggerEventExtraInfo) { + var _a; + pauseScheduling(); + for (const effect2 of dep.keys()) { + let tracking; + if (effect2._dirtyLevel < dirtyLevel && (tracking != null ? tracking : tracking = dep.get(effect2) === effect2._trackId)) { + effect2._shouldSchedule || (effect2._shouldSchedule = effect2._dirtyLevel === 0); + effect2._dirtyLevel = dirtyLevel; + } + if (effect2._shouldSchedule && (tracking != null ? tracking : tracking = dep.get(effect2) === effect2._trackId)) { + { + (_a = effect2.onTrigger) == null ? void 0 : _a.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo)); + } + effect2.trigger(); + if ((!effect2._runnings || effect2.allowRecurse) && effect2._dirtyLevel !== 2) { + effect2._shouldSchedule = false; + if (effect2.scheduler) { + queueEffectSchedulers.push(effect2.scheduler); + } + } + } + } + resetScheduling(); + } + + const createDep = (cleanup, computed) => { + const dep = /* @__PURE__ */ new Map(); + dep.cleanup = cleanup; + dep.computed = computed; + return dep; + }; + + const targetMap = /* @__PURE__ */ new WeakMap(); + const ITERATE_KEY = Symbol("iterate" ); + const MAP_KEY_ITERATE_KEY = Symbol("Map key iterate" ); + function track(target, type, key) { + if (shouldTrack && activeEffect) { + let depsMap = targetMap.get(target); + if (!depsMap) { + targetMap.set(target, depsMap = /* @__PURE__ */ new Map()); + } + let dep = depsMap.get(key); + if (!dep) { + depsMap.set(key, dep = createDep(() => depsMap.delete(key))); + } + trackEffect( + activeEffect, + dep, + { + target, + type, + key + } + ); + } + } + function trigger(target, type, key, newValue, oldValue, oldTarget) { + const depsMap = targetMap.get(target); + if (!depsMap) { + return; + } + let deps = []; + if (type === "clear") { + deps = [...depsMap.values()]; + } else if (key === "length" && isArray(target)) { + const newLength = Number(newValue); + depsMap.forEach((dep, key2) => { + if (key2 === "length" || !isSymbol(key2) && key2 >= newLength) { + deps.push(dep); + } + }); + } else { + if (key !== void 0) { + deps.push(depsMap.get(key)); + } + switch (type) { + case "add": + if (!isArray(target)) { + deps.push(depsMap.get(ITERATE_KEY)); + if (isMap(target)) { + deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)); + } + } else if (isIntegerKey(key)) { + deps.push(depsMap.get("length")); + } + break; + case "delete": + if (!isArray(target)) { + deps.push(depsMap.get(ITERATE_KEY)); + if (isMap(target)) { + deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)); + } + } + break; + case "set": + if (isMap(target)) { + deps.push(depsMap.get(ITERATE_KEY)); + } + break; + } + } + pauseScheduling(); + for (const dep of deps) { + if (dep) { + triggerEffects( + dep, + 4, + { + target, + type, + key, + newValue, + oldValue, + oldTarget + } + ); + } + } + resetScheduling(); + } + function getDepFromReactive(object, key) { + var _a; + return (_a = targetMap.get(object)) == null ? void 0 : _a.get(key); + } + + const isNonTrackableKeys = /* @__PURE__ */ makeMap(`__proto__,__v_isRef,__isVue`); + const builtInSymbols = new Set( + /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol) + ); + const arrayInstrumentations = /* @__PURE__ */ createArrayInstrumentations(); + function createArrayInstrumentations() { + const instrumentations = {}; + ["includes", "indexOf", "lastIndexOf"].forEach((key) => { + instrumentations[key] = function(...args) { + const arr = toRaw(this); + for (let i = 0, l = this.length; i < l; i++) { + track(arr, "get", i + ""); + } + const res = arr[key](...args); + if (res === -1 || res === false) { + return arr[key](...args.map(toRaw)); + } else { + return res; + } + }; + }); + ["push", "pop", "shift", "unshift", "splice"].forEach((key) => { + instrumentations[key] = function(...args) { + pauseTracking(); + pauseScheduling(); + const res = toRaw(this)[key].apply(this, args); + resetScheduling(); + resetTracking(); + return res; + }; + }); + return instrumentations; + } + function hasOwnProperty(key) { + const obj = toRaw(this); + track(obj, "has", key); + return obj.hasOwnProperty(key); + } + class BaseReactiveHandler { + constructor(_isReadonly = false, _shallow = false) { + this._isReadonly = _isReadonly; + this._shallow = _shallow; + } + get(target, key, receiver) { + const isReadonly2 = this._isReadonly, shallow = this._shallow; + if (key === "__v_isReactive") { + return !isReadonly2; + } else if (key === "__v_isReadonly") { + return isReadonly2; + } else if (key === "__v_isShallow") { + return shallow; + } else if (key === "__v_raw") { + if (receiver === (isReadonly2 ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype + // this means the reciever is a user proxy of the reactive proxy + Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) { + return target; + } + return; + } + const targetIsArray = isArray(target); + if (!isReadonly2) { + if (targetIsArray && hasOwn(arrayInstrumentations, key)) { + return Reflect.get(arrayInstrumentations, key, receiver); + } + if (key === "hasOwnProperty") { + return hasOwnProperty; + } + } + const res = Reflect.get(target, key, receiver); + if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { + return res; + } + if (!isReadonly2) { + track(target, "get", key); + } + if (shallow) { + return res; + } + if (isRef(res)) { + return targetIsArray && isIntegerKey(key) ? res : res.value; + } + if (isObject(res)) { + return isReadonly2 ? readonly(res) : reactive(res); + } + return res; + } + } + class MutableReactiveHandler extends BaseReactiveHandler { + constructor(shallow = false) { + super(false, shallow); + } + set(target, key, value, receiver) { + let oldValue = target[key]; + if (!this._shallow) { + const isOldValueReadonly = isReadonly(oldValue); + if (!isShallow(value) && !isReadonly(value)) { + oldValue = toRaw(oldValue); + value = toRaw(value); + } + if (!isArray(target) && isRef(oldValue) && !isRef(value)) { + if (isOldValueReadonly) { + return false; + } else { + oldValue.value = value; + return true; + } + } + } + const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key); + const result = Reflect.set(target, key, value, receiver); + if (target === toRaw(receiver)) { + if (!hadKey) { + trigger(target, "add", key, value); + } else if (hasChanged(value, oldValue)) { + trigger(target, "set", key, value, oldValue); + } + } + return result; + } + deleteProperty(target, key) { + const hadKey = hasOwn(target, key); + const oldValue = target[key]; + const result = Reflect.deleteProperty(target, key); + if (result && hadKey) { + trigger(target, "delete", key, void 0, oldValue); + } + return result; + } + has(target, key) { + const result = Reflect.has(target, key); + if (!isSymbol(key) || !builtInSymbols.has(key)) { + track(target, "has", key); + } + return result; + } + ownKeys(target) { + track( + target, + "iterate", + isArray(target) ? "length" : ITERATE_KEY + ); + return Reflect.ownKeys(target); + } + } + class ReadonlyReactiveHandler extends BaseReactiveHandler { + constructor(shallow = false) { + super(true, shallow); + } + set(target, key) { + { + warn$2( + `Set operation on key "${String(key)}" failed: target is readonly.`, + target + ); + } + return true; + } + deleteProperty(target, key) { + { + warn$2( + `Delete operation on key "${String(key)}" failed: target is readonly.`, + target + ); + } + return true; + } + } + const mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler(); + const readonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(); + const shallowReactiveHandlers = /* @__PURE__ */ new MutableReactiveHandler( + true + ); + const shallowReadonlyHandlers = /* @__PURE__ */ new ReadonlyReactiveHandler(true); + + const toShallow = (value) => value; + const getProto = (v) => Reflect.getPrototypeOf(v); + function get(target, key, isReadonly = false, isShallow = false) { + target = target["__v_raw"]; + const rawTarget = toRaw(target); + const rawKey = toRaw(key); + if (!isReadonly) { + if (hasChanged(key, rawKey)) { + track(rawTarget, "get", key); + } + track(rawTarget, "get", rawKey); + } + const { has: has2 } = getProto(rawTarget); + const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive; + if (has2.call(rawTarget, key)) { + return wrap(target.get(key)); + } else if (has2.call(rawTarget, rawKey)) { + return wrap(target.get(rawKey)); + } else if (target !== rawTarget) { + target.get(key); + } + } + function has(key, isReadonly = false) { + const target = this["__v_raw"]; + const rawTarget = toRaw(target); + const rawKey = toRaw(key); + if (!isReadonly) { + if (hasChanged(key, rawKey)) { + track(rawTarget, "has", key); + } + track(rawTarget, "has", rawKey); + } + return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey); + } + function size(target, isReadonly = false) { + target = target["__v_raw"]; + !isReadonly && track(toRaw(target), "iterate", ITERATE_KEY); + return Reflect.get(target, "size", target); + } + function add(value) { + value = toRaw(value); + const target = toRaw(this); + const proto = getProto(target); + const hadKey = proto.has.call(target, value); + if (!hadKey) { + target.add(value); + trigger(target, "add", value, value); + } + return this; + } + function set(key, value) { + value = toRaw(value); + const target = toRaw(this); + const { has: has2, get: get2 } = getProto(target); + let hadKey = has2.call(target, key); + if (!hadKey) { + key = toRaw(key); + hadKey = has2.call(target, key); + } else { + checkIdentityKeys(target, has2, key); + } + const oldValue = get2.call(target, key); + target.set(key, value); + if (!hadKey) { + trigger(target, "add", key, value); + } else if (hasChanged(value, oldValue)) { + trigger(target, "set", key, value, oldValue); + } + return this; + } + function deleteEntry(key) { + const target = toRaw(this); + const { has: has2, get: get2 } = getProto(target); + let hadKey = has2.call(target, key); + if (!hadKey) { + key = toRaw(key); + hadKey = has2.call(target, key); + } else { + checkIdentityKeys(target, has2, key); + } + const oldValue = get2 ? get2.call(target, key) : void 0; + const result = target.delete(key); + if (hadKey) { + trigger(target, "delete", key, void 0, oldValue); + } + return result; + } + function clear() { + const target = toRaw(this); + const hadItems = target.size !== 0; + const oldTarget = isMap(target) ? new Map(target) : new Set(target) ; + const result = target.clear(); + if (hadItems) { + trigger(target, "clear", void 0, void 0, oldTarget); + } + return result; + } + function createForEach(isReadonly, isShallow) { + return function forEach(callback, thisArg) { + const observed = this; + const target = observed["__v_raw"]; + const rawTarget = toRaw(target); + const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive; + !isReadonly && track(rawTarget, "iterate", ITERATE_KEY); + return target.forEach((value, key) => { + return callback.call(thisArg, wrap(value), wrap(key), observed); + }); + }; + } + function createIterableMethod(method, isReadonly, isShallow) { + return function(...args) { + const target = this["__v_raw"]; + const rawTarget = toRaw(target); + const targetIsMap = isMap(rawTarget); + const isPair = method === "entries" || method === Symbol.iterator && targetIsMap; + const isKeyOnly = method === "keys" && targetIsMap; + const innerIterator = target[method](...args); + const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive; + !isReadonly && track( + rawTarget, + "iterate", + isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY + ); + return { + // iterator protocol + next() { + const { value, done } = innerIterator.next(); + return done ? { value, done } : { + value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), + done + }; + }, + // iterable protocol + [Symbol.iterator]() { + return this; + } + }; + }; + } + function createReadonlyMethod(type) { + return function(...args) { + { + const key = args[0] ? `on key "${args[0]}" ` : ``; + console.warn( + `${capitalize(type)} operation ${key}failed: target is readonly.`, + toRaw(this) + ); + } + return type === "delete" ? false : type === "clear" ? void 0 : this; + }; + } + function createInstrumentations() { + const mutableInstrumentations2 = { + get(key) { + return get(this, key); + }, + get size() { + return size(this); + }, + has, + add, + set, + delete: deleteEntry, + clear, + forEach: createForEach(false, false) + }; + const shallowInstrumentations2 = { + get(key) { + return get(this, key, false, true); + }, + get size() { + return size(this); + }, + has, + add, + set, + delete: deleteEntry, + clear, + forEach: createForEach(false, true) + }; + const readonlyInstrumentations2 = { + get(key) { + return get(this, key, true); + }, + get size() { + return size(this, true); + }, + has(key) { + return has.call(this, key, true); + }, + add: createReadonlyMethod("add"), + set: createReadonlyMethod("set"), + delete: createReadonlyMethod("delete"), + clear: createReadonlyMethod("clear"), + forEach: createForEach(true, false) + }; + const shallowReadonlyInstrumentations2 = { + get(key) { + return get(this, key, true, true); + }, + get size() { + return size(this, true); + }, + has(key) { + return has.call(this, key, true); + }, + add: createReadonlyMethod("add"), + set: createReadonlyMethod("set"), + delete: createReadonlyMethod("delete"), + clear: createReadonlyMethod("clear"), + forEach: createForEach(true, true) + }; + const iteratorMethods = ["keys", "values", "entries", Symbol.iterator]; + iteratorMethods.forEach((method) => { + mutableInstrumentations2[method] = createIterableMethod( + method, + false, + false + ); + readonlyInstrumentations2[method] = createIterableMethod( + method, + true, + false + ); + shallowInstrumentations2[method] = createIterableMethod( + method, + false, + true + ); + shallowReadonlyInstrumentations2[method] = createIterableMethod( + method, + true, + true + ); + }); + return [ + mutableInstrumentations2, + readonlyInstrumentations2, + shallowInstrumentations2, + shallowReadonlyInstrumentations2 + ]; + } + const [ + mutableInstrumentations, + readonlyInstrumentations, + shallowInstrumentations, + shallowReadonlyInstrumentations + ] = /* @__PURE__ */ createInstrumentations(); + function createInstrumentationGetter(isReadonly, shallow) { + const instrumentations = shallow ? isReadonly ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly ? readonlyInstrumentations : mutableInstrumentations; + return (target, key, receiver) => { + if (key === "__v_isReactive") { + return !isReadonly; + } else if (key === "__v_isReadonly") { + return isReadonly; + } else if (key === "__v_raw") { + return target; + } + return Reflect.get( + hasOwn(instrumentations, key) && key in target ? instrumentations : target, + key, + receiver + ); + }; + } + const mutableCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(false, false) + }; + const shallowCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(false, true) + }; + const readonlyCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(true, false) + }; + const shallowReadonlyCollectionHandlers = { + get: /* @__PURE__ */ createInstrumentationGetter(true, true) + }; + function checkIdentityKeys(target, has2, key) { + const rawKey = toRaw(key); + if (rawKey !== key && has2.call(target, rawKey)) { + const type = toRawType(target); + console.warn( + `Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.` + ); + } + } + + const reactiveMap = /* @__PURE__ */ new WeakMap(); + const shallowReactiveMap = /* @__PURE__ */ new WeakMap(); + const readonlyMap = /* @__PURE__ */ new WeakMap(); + const shallowReadonlyMap = /* @__PURE__ */ new WeakMap(); + function targetTypeMap(rawType) { + switch (rawType) { + case "Object": + case "Array": + return 1 /* COMMON */; + case "Map": + case "Set": + case "WeakMap": + case "WeakSet": + return 2 /* COLLECTION */; + default: + return 0 /* INVALID */; + } + } + function getTargetType(value) { + return value["__v_skip"] || !Object.isExtensible(value) ? 0 /* INVALID */ : targetTypeMap(toRawType(value)); + } + function reactive(target) { + if (isReadonly(target)) { + return target; + } + return createReactiveObject( + target, + false, + mutableHandlers, + mutableCollectionHandlers, + reactiveMap + ); + } + function shallowReactive(target) { + return createReactiveObject( + target, + false, + shallowReactiveHandlers, + shallowCollectionHandlers, + shallowReactiveMap + ); + } + function readonly(target) { + return createReactiveObject( + target, + true, + readonlyHandlers, + readonlyCollectionHandlers, + readonlyMap + ); + } + function shallowReadonly(target) { + return createReactiveObject( + target, + true, + shallowReadonlyHandlers, + shallowReadonlyCollectionHandlers, + shallowReadonlyMap + ); + } + function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) { + if (!isObject(target)) { + { + console.warn(`value cannot be made reactive: ${String(target)}`); + } + return target; + } + if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) { + return target; + } + const existingProxy = proxyMap.get(target); + if (existingProxy) { + return existingProxy; + } + const targetType = getTargetType(target); + if (targetType === 0 /* INVALID */) { + return target; + } + const proxy = new Proxy( + target, + targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers + ); + proxyMap.set(target, proxy); + return proxy; + } + function isReactive(value) { + if (isReadonly(value)) { + return isReactive(value["__v_raw"]); + } + return !!(value && value["__v_isReactive"]); + } + function isReadonly(value) { + return !!(value && value["__v_isReadonly"]); + } + function isShallow(value) { + return !!(value && value["__v_isShallow"]); + } + function isProxy(value) { + return isReactive(value) || isReadonly(value); + } + function toRaw(observed) { + const raw = observed && observed["__v_raw"]; + return raw ? toRaw(raw) : observed; + } + function markRaw(value) { + if (Object.isExtensible(value)) { + def(value, "__v_skip", true); + } + return value; + } + const toReactive = (value) => isObject(value) ? reactive(value) : value; + const toReadonly = (value) => isObject(value) ? readonly(value) : value; + + class ComputedRefImpl { + constructor(getter, _setter, isReadonly, isSSR) { + this._setter = _setter; + this.dep = void 0; + this.__v_isRef = true; + this["__v_isReadonly"] = false; + this.effect = new ReactiveEffect( + () => getter(this._value), + () => triggerRefValue( + this, + this.effect._dirtyLevel === 2 ? 2 : 3 + ) + ); + this.effect.computed = this; + this.effect.active = this._cacheable = !isSSR; + this["__v_isReadonly"] = isReadonly; + } + get value() { + const self = toRaw(this); + if ((!self._cacheable || self.effect.dirty) && hasChanged(self._value, self._value = self.effect.run())) { + triggerRefValue(self, 4); + } + trackRefValue(self); + if (self.effect._dirtyLevel >= 2) { + triggerRefValue(self, 2); + } + return self._value; + } + set value(newValue) { + this._setter(newValue); + } + // #region polyfill _dirty for backward compatibility third party code for Vue <= 3.3.x + get _dirty() { + return this.effect.dirty; + } + set _dirty(v) { + this.effect.dirty = v; + } + // #endregion + } + function computed$1(getterOrOptions, debugOptions, isSSR = false) { + let getter; + let setter; + const onlyGetter = isFunction(getterOrOptions); + if (onlyGetter) { + getter = getterOrOptions; + setter = () => { + console.warn("Write operation failed: computed value is readonly"); + } ; + } else { + getter = getterOrOptions.get; + setter = getterOrOptions.set; + } + const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR); + if (debugOptions && !isSSR) { + cRef.effect.onTrack = debugOptions.onTrack; + cRef.effect.onTrigger = debugOptions.onTrigger; + } + return cRef; + } + + function trackRefValue(ref2) { + var _a; + if (shouldTrack && activeEffect) { + ref2 = toRaw(ref2); + trackEffect( + activeEffect, + (_a = ref2.dep) != null ? _a : ref2.dep = createDep( + () => ref2.dep = void 0, + ref2 instanceof ComputedRefImpl ? ref2 : void 0 + ), + { + target: ref2, + type: "get", + key: "value" + } + ); + } + } + function triggerRefValue(ref2, dirtyLevel = 4, newVal) { + ref2 = toRaw(ref2); + const dep = ref2.dep; + if (dep) { + triggerEffects( + dep, + dirtyLevel, + { + target: ref2, + type: "set", + key: "value", + newValue: newVal + } + ); + } + } + function isRef(r) { + return !!(r && r.__v_isRef === true); + } + function ref(value) { + return createRef(value, false); + } + function shallowRef(value) { + return createRef(value, true); + } + function createRef(rawValue, shallow) { + if (isRef(rawValue)) { + return rawValue; + } + return new RefImpl(rawValue, shallow); + } + class RefImpl { + constructor(value, __v_isShallow) { + this.__v_isShallow = __v_isShallow; + this.dep = void 0; + this.__v_isRef = true; + this._rawValue = __v_isShallow ? value : toRaw(value); + this._value = __v_isShallow ? value : toReactive(value); + } + get value() { + trackRefValue(this); + return this._value; + } + set value(newVal) { + const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal); + newVal = useDirectValue ? newVal : toRaw(newVal); + if (hasChanged(newVal, this._rawValue)) { + this._rawValue = newVal; + this._value = useDirectValue ? newVal : toReactive(newVal); + triggerRefValue(this, 4, newVal); + } + } + } + function triggerRef(ref2) { + triggerRefValue(ref2, 4, ref2.value ); + } + function unref(ref2) { + return isRef(ref2) ? ref2.value : ref2; + } + function toValue(source) { + return isFunction(source) ? source() : unref(source); + } + const shallowUnwrapHandlers = { + get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)), + set: (target, key, value, receiver) => { + const oldValue = target[key]; + if (isRef(oldValue) && !isRef(value)) { + oldValue.value = value; + return true; + } else { + return Reflect.set(target, key, value, receiver); + } + } + }; + function proxyRefs(objectWithRefs) { + return isReactive(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers); + } + class CustomRefImpl { + constructor(factory) { + this.dep = void 0; + this.__v_isRef = true; + const { get, set } = factory( + () => trackRefValue(this), + () => triggerRefValue(this) + ); + this._get = get; + this._set = set; + } + get value() { + return this._get(); + } + set value(newVal) { + this._set(newVal); + } + } + function customRef(factory) { + return new CustomRefImpl(factory); + } + function toRefs(object) { + if (!isProxy(object)) { + console.warn(`toRefs() expects a reactive object but received a plain one.`); + } + const ret = isArray(object) ? new Array(object.length) : {}; + for (const key in object) { + ret[key] = propertyToRef(object, key); + } + return ret; + } + class ObjectRefImpl { + constructor(_object, _key, _defaultValue) { + this._object = _object; + this._key = _key; + this._defaultValue = _defaultValue; + this.__v_isRef = true; + } + get value() { + const val = this._object[this._key]; + return val === void 0 ? this._defaultValue : val; + } + set value(newVal) { + this._object[this._key] = newVal; + } + get dep() { + return getDepFromReactive(toRaw(this._object), this._key); + } + } + class GetterRefImpl { + constructor(_getter) { + this._getter = _getter; + this.__v_isRef = true; + this.__v_isReadonly = true; + } + get value() { + return this._getter(); + } + } + function toRef(source, key, defaultValue) { + if (isRef(source)) { + return source; + } else if (isFunction(source)) { + return new GetterRefImpl(source); + } else if (isObject(source) && arguments.length > 1) { + return propertyToRef(source, key, defaultValue); + } else { + return ref(source); + } + } + function propertyToRef(source, key, defaultValue) { + const val = source[key]; + return isRef(val) ? val : new ObjectRefImpl(source, key, defaultValue); + } + + const TrackOpTypes = { + "GET": "get", + "HAS": "has", + "ITERATE": "iterate" + }; + const TriggerOpTypes = { + "SET": "set", + "ADD": "add", + "DELETE": "delete", + "CLEAR": "clear" + }; + + const stack$1 = []; + function pushWarningContext(vnode) { + stack$1.push(vnode); + } + function popWarningContext() { + stack$1.pop(); + } + function warn$1(msg, ...args) { + pauseTracking(); + const instance = stack$1.length ? stack$1[stack$1.length - 1].component : null; + const appWarnHandler = instance && instance.appContext.config.warnHandler; + const trace = getComponentTrace(); + if (appWarnHandler) { + callWithErrorHandling( + appWarnHandler, + instance, + 11, + [ + msg + args.join(""), + instance && instance.proxy, + trace.map( + ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>` + ).join("\n"), + trace + ] + ); + } else { + const warnArgs = [`[Vue warn]: ${msg}`, ...args]; + if (trace.length && // avoid spamming console during tests + true) { + warnArgs.push(` +`, ...formatTrace(trace)); + } + console.warn(...warnArgs); + } + resetTracking(); + } + function getComponentTrace() { + let currentVNode = stack$1[stack$1.length - 1]; + if (!currentVNode) { + return []; + } + const normalizedStack = []; + while (currentVNode) { + const last = normalizedStack[0]; + if (last && last.vnode === currentVNode) { + last.recurseCount++; + } else { + normalizedStack.push({ + vnode: currentVNode, + recurseCount: 0 + }); + } + const parentInstance = currentVNode.component && currentVNode.component.parent; + currentVNode = parentInstance && parentInstance.vnode; + } + return normalizedStack; + } + function formatTrace(trace) { + const logs = []; + trace.forEach((entry, i) => { + logs.push(...i === 0 ? [] : [` +`], ...formatTraceEntry(entry)); + }); + return logs; + } + function formatTraceEntry({ vnode, recurseCount }) { + const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``; + const isRoot = vnode.component ? vnode.component.parent == null : false; + const open = ` at <${formatComponentName( + vnode.component, + vnode.type, + isRoot + )}`; + const close = `>` + postfix; + return vnode.props ? [open, ...formatProps(vnode.props), close] : [open + close]; + } + function formatProps(props) { + const res = []; + const keys = Object.keys(props); + keys.slice(0, 3).forEach((key) => { + res.push(...formatProp(key, props[key])); + }); + if (keys.length > 3) { + res.push(` ...`); + } + return res; + } + function formatProp(key, value, raw) { + if (isString(value)) { + value = JSON.stringify(value); + return raw ? value : [`${key}=${value}`]; + } else if (typeof value === "number" || typeof value === "boolean" || value == null) { + return raw ? value : [`${key}=${value}`]; + } else if (isRef(value)) { + value = formatProp(key, toRaw(value.value), true); + return raw ? value : [`${key}=Ref<`, value, `>`]; + } else if (isFunction(value)) { + return [`${key}=fn${value.name ? `<${value.name}>` : ``}`]; + } else { + value = toRaw(value); + return raw ? value : [`${key}=`, value]; + } + } + function assertNumber(val, type) { + if (val === void 0) { + return; + } else if (typeof val !== "number") { + warn$1(`${type} is not a valid number - got ${JSON.stringify(val)}.`); + } else if (isNaN(val)) { + warn$1(`${type} is NaN - the duration expression might be incorrect.`); + } + } + + const ErrorCodes = { + "SETUP_FUNCTION": 0, + "0": "SETUP_FUNCTION", + "RENDER_FUNCTION": 1, + "1": "RENDER_FUNCTION", + "WATCH_GETTER": 2, + "2": "WATCH_GETTER", + "WATCH_CALLBACK": 3, + "3": "WATCH_CALLBACK", + "WATCH_CLEANUP": 4, + "4": "WATCH_CLEANUP", + "NATIVE_EVENT_HANDLER": 5, + "5": "NATIVE_EVENT_HANDLER", + "COMPONENT_EVENT_HANDLER": 6, + "6": "COMPONENT_EVENT_HANDLER", + "VNODE_HOOK": 7, + "7": "VNODE_HOOK", + "DIRECTIVE_HOOK": 8, + "8": "DIRECTIVE_HOOK", + "TRANSITION_HOOK": 9, + "9": "TRANSITION_HOOK", + "APP_ERROR_HANDLER": 10, + "10": "APP_ERROR_HANDLER", + "APP_WARN_HANDLER": 11, + "11": "APP_WARN_HANDLER", + "FUNCTION_REF": 12, + "12": "FUNCTION_REF", + "ASYNC_COMPONENT_LOADER": 13, + "13": "ASYNC_COMPONENT_LOADER", + "SCHEDULER": 14, + "14": "SCHEDULER" + }; + const ErrorTypeStrings$1 = { + ["sp"]: "serverPrefetch hook", + ["bc"]: "beforeCreate hook", + ["c"]: "created hook", + ["bm"]: "beforeMount hook", + ["m"]: "mounted hook", + ["bu"]: "beforeUpdate hook", + ["u"]: "updated", + ["bum"]: "beforeUnmount hook", + ["um"]: "unmounted hook", + ["a"]: "activated hook", + ["da"]: "deactivated hook", + ["ec"]: "errorCaptured hook", + ["rtc"]: "renderTracked hook", + ["rtg"]: "renderTriggered hook", + [0]: "setup function", + [1]: "render function", + [2]: "watcher getter", + [3]: "watcher callback", + [4]: "watcher cleanup function", + [5]: "native event handler", + [6]: "component event handler", + [7]: "vnode hook", + [8]: "directive hook", + [9]: "transition hook", + [10]: "app errorHandler", + [11]: "app warnHandler", + [12]: "ref function", + [13]: "async component loader", + [14]: "scheduler flush. This is likely a Vue internals bug. Please open an issue at https://github.com/vuejs/core ." + }; + function callWithErrorHandling(fn, instance, type, args) { + let res; + try { + res = args ? fn(...args) : fn(); + } catch (err) { + handleError(err, instance, type); + } + return res; + } + function callWithAsyncErrorHandling(fn, instance, type, args) { + if (isFunction(fn)) { + const res = callWithErrorHandling(fn, instance, type, args); + if (res && isPromise(res)) { + res.catch((err) => { + handleError(err, instance, type); + }); + } + return res; + } + const values = []; + for (let i = 0; i < fn.length; i++) { + values.push(callWithAsyncErrorHandling(fn[i], instance, type, args)); + } + return values; + } + function handleError(err, instance, type, throwInDev = true) { + const contextVNode = instance ? instance.vnode : null; + if (instance) { + let cur = instance.parent; + const exposedInstance = instance.proxy; + const errorInfo = ErrorTypeStrings$1[type] ; + while (cur) { + const errorCapturedHooks = cur.ec; + if (errorCapturedHooks) { + for (let i = 0; i < errorCapturedHooks.length; i++) { + if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) { + return; + } + } + } + cur = cur.parent; + } + const appErrorHandler = instance.appContext.config.errorHandler; + if (appErrorHandler) { + callWithErrorHandling( + appErrorHandler, + null, + 10, + [err, exposedInstance, errorInfo] + ); + return; + } + } + logError(err, type, contextVNode, throwInDev); + } + function logError(err, type, contextVNode, throwInDev = true) { + { + const info = ErrorTypeStrings$1[type]; + if (contextVNode) { + pushWarningContext(contextVNode); + } + warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`); + if (contextVNode) { + popWarningContext(); + } + if (throwInDev) { + throw err; + } else { + console.error(err); + } + } + } + + let isFlushing = false; + let isFlushPending = false; + const queue = []; + let flushIndex = 0; + const pendingPostFlushCbs = []; + let activePostFlushCbs = null; + let postFlushIndex = 0; + const resolvedPromise = /* @__PURE__ */ Promise.resolve(); + let currentFlushPromise = null; + const RECURSION_LIMIT = 100; + function nextTick(fn) { + const p = currentFlushPromise || resolvedPromise; + return fn ? p.then(this ? fn.bind(this) : fn) : p; + } + function findInsertionIndex(id) { + let start = flushIndex + 1; + let end = queue.length; + while (start < end) { + const middle = start + end >>> 1; + const middleJob = queue[middle]; + const middleJobId = getId(middleJob); + if (middleJobId < id || middleJobId === id && middleJob.pre) { + start = middle + 1; + } else { + end = middle; + } + } + return start; + } + function queueJob(job) { + if (!queue.length || !queue.includes( + job, + isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex + )) { + if (job.id == null) { + queue.push(job); + } else { + queue.splice(findInsertionIndex(job.id), 0, job); + } + queueFlush(); + } + } + function queueFlush() { + if (!isFlushing && !isFlushPending) { + isFlushPending = true; + currentFlushPromise = resolvedPromise.then(flushJobs); + } + } + function invalidateJob(job) { + const i = queue.indexOf(job); + if (i > flushIndex) { + queue.splice(i, 1); + } + } + function queuePostFlushCb(cb) { + if (!isArray(cb)) { + if (!activePostFlushCbs || !activePostFlushCbs.includes( + cb, + cb.allowRecurse ? postFlushIndex + 1 : postFlushIndex + )) { + pendingPostFlushCbs.push(cb); + } + } else { + pendingPostFlushCbs.push(...cb); + } + queueFlush(); + } + function flushPreFlushCbs(instance, seen, i = isFlushing ? flushIndex + 1 : 0) { + { + seen = seen || /* @__PURE__ */ new Map(); + } + for (; i < queue.length; i++) { + const cb = queue[i]; + if (cb && cb.pre) { + if (instance && cb.id !== instance.uid) { + continue; + } + if (checkRecursiveUpdates(seen, cb)) { + continue; + } + queue.splice(i, 1); + i--; + cb(); + } + } + } + function flushPostFlushCbs(seen) { + if (pendingPostFlushCbs.length) { + const deduped = [...new Set(pendingPostFlushCbs)].sort( + (a, b) => getId(a) - getId(b) + ); + pendingPostFlushCbs.length = 0; + if (activePostFlushCbs) { + activePostFlushCbs.push(...deduped); + return; + } + activePostFlushCbs = deduped; + { + seen = seen || /* @__PURE__ */ new Map(); + } + for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) { + if (checkRecursiveUpdates(seen, activePostFlushCbs[postFlushIndex])) { + continue; + } + activePostFlushCbs[postFlushIndex](); + } + activePostFlushCbs = null; + postFlushIndex = 0; + } + } + const getId = (job) => job.id == null ? Infinity : job.id; + const comparator = (a, b) => { + const diff = getId(a) - getId(b); + if (diff === 0) { + if (a.pre && !b.pre) + return -1; + if (b.pre && !a.pre) + return 1; + } + return diff; + }; + function flushJobs(seen) { + isFlushPending = false; + isFlushing = true; + { + seen = seen || /* @__PURE__ */ new Map(); + } + queue.sort(comparator); + const check = (job) => checkRecursiveUpdates(seen, job) ; + try { + for (flushIndex = 0; flushIndex < queue.length; flushIndex++) { + const job = queue[flushIndex]; + if (job && job.active !== false) { + if (check(job)) { + continue; + } + callWithErrorHandling(job, null, 14); + } + } + } finally { + flushIndex = 0; + queue.length = 0; + flushPostFlushCbs(seen); + isFlushing = false; + currentFlushPromise = null; + if (queue.length || pendingPostFlushCbs.length) { + flushJobs(seen); + } + } + } + function checkRecursiveUpdates(seen, fn) { + if (!seen.has(fn)) { + seen.set(fn, 1); + } else { + const count = seen.get(fn); + if (count > RECURSION_LIMIT) { + const instance = fn.ownerInstance; + const componentName = instance && getComponentName(instance.type); + handleError( + `Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`, + null, + 10 + ); + return true; + } else { + seen.set(fn, count + 1); + } + } + } + + let isHmrUpdating = false; + const hmrDirtyComponents = /* @__PURE__ */ new Set(); + { + getGlobalThis().__VUE_HMR_RUNTIME__ = { + createRecord: tryWrap(createRecord), + rerender: tryWrap(rerender), + reload: tryWrap(reload) + }; + } + const map = /* @__PURE__ */ new Map(); + function registerHMR(instance) { + const id = instance.type.__hmrId; + let record = map.get(id); + if (!record) { + createRecord(id, instance.type); + record = map.get(id); + } + record.instances.add(instance); + } + function unregisterHMR(instance) { + map.get(instance.type.__hmrId).instances.delete(instance); + } + function createRecord(id, initialDef) { + if (map.has(id)) { + return false; + } + map.set(id, { + initialDef: normalizeClassComponent(initialDef), + instances: /* @__PURE__ */ new Set() + }); + return true; + } + function normalizeClassComponent(component) { + return isClassComponent(component) ? component.__vccOpts : component; + } + function rerender(id, newRender) { + const record = map.get(id); + if (!record) { + return; + } + record.initialDef.render = newRender; + [...record.instances].forEach((instance) => { + if (newRender) { + instance.render = newRender; + normalizeClassComponent(instance.type).render = newRender; + } + instance.renderCache = []; + isHmrUpdating = true; + instance.effect.dirty = true; + instance.update(); + isHmrUpdating = false; + }); + } + function reload(id, newComp) { + const record = map.get(id); + if (!record) + return; + newComp = normalizeClassComponent(newComp); + updateComponentDef(record.initialDef, newComp); + const instances = [...record.instances]; + for (const instance of instances) { + const oldComp = normalizeClassComponent(instance.type); + if (!hmrDirtyComponents.has(oldComp)) { + if (oldComp !== record.initialDef) { + updateComponentDef(oldComp, newComp); + } + hmrDirtyComponents.add(oldComp); + } + instance.appContext.propsCache.delete(instance.type); + instance.appContext.emitsCache.delete(instance.type); + instance.appContext.optionsCache.delete(instance.type); + if (instance.ceReload) { + hmrDirtyComponents.add(oldComp); + instance.ceReload(newComp.styles); + hmrDirtyComponents.delete(oldComp); + } else if (instance.parent) { + instance.parent.effect.dirty = true; + queueJob(instance.parent.update); + } else if (instance.appContext.reload) { + instance.appContext.reload(); + } else if (typeof window !== "undefined") { + window.location.reload(); + } else { + console.warn( + "[HMR] Root or manually mounted instance modified. Full reload required." + ); + } + } + queuePostFlushCb(() => { + for (const instance of instances) { + hmrDirtyComponents.delete( + normalizeClassComponent(instance.type) + ); + } + }); + } + function updateComponentDef(oldComp, newComp) { + extend(oldComp, newComp); + for (const key in oldComp) { + if (key !== "__file" && !(key in newComp)) { + delete oldComp[key]; + } + } + } + function tryWrap(fn) { + return (id, arg) => { + try { + return fn(id, arg); + } catch (e) { + console.error(e); + console.warn( + `[HMR] Something went wrong during Vue component hot-reload. Full reload required.` + ); + } + }; + } + + let devtools$1; + let buffer = []; + let devtoolsNotInstalled = false; + function emit$1(event, ...args) { + if (devtools$1) { + devtools$1.emit(event, ...args); + } else if (!devtoolsNotInstalled) { + buffer.push({ event, args }); + } + } + function setDevtoolsHook$1(hook, target) { + var _a, _b; + devtools$1 = hook; + if (devtools$1) { + devtools$1.enabled = true; + buffer.forEach(({ event, args }) => devtools$1.emit(event, ...args)); + buffer = []; + } else if ( + // handle late devtools injection - only do this if we are in an actual + // browser environment to avoid the timer handle stalling test runner exit + // (#4815) + typeof window !== "undefined" && // some envs mock window but not fully + window.HTMLElement && // also exclude jsdom + !((_b = (_a = window.navigator) == null ? void 0 : _a.userAgent) == null ? void 0 : _b.includes("jsdom")) + ) { + const replay = target.__VUE_DEVTOOLS_HOOK_REPLAY__ = target.__VUE_DEVTOOLS_HOOK_REPLAY__ || []; + replay.push((newHook) => { + setDevtoolsHook$1(newHook, target); + }); + setTimeout(() => { + if (!devtools$1) { + target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null; + devtoolsNotInstalled = true; + buffer = []; + } + }, 3e3); + } else { + devtoolsNotInstalled = true; + buffer = []; + } + } + function devtoolsInitApp(app, version) { + emit$1("app:init" /* APP_INIT */, app, version, { + Fragment, + Text, + Comment, + Static + }); + } + function devtoolsUnmountApp(app) { + emit$1("app:unmount" /* APP_UNMOUNT */, app); + } + const devtoolsComponentAdded = /* @__PURE__ */ createDevtoolsComponentHook( + "component:added" /* COMPONENT_ADDED */ + ); + const devtoolsComponentUpdated = /* @__PURE__ */ createDevtoolsComponentHook("component:updated" /* COMPONENT_UPDATED */); + const _devtoolsComponentRemoved = /* @__PURE__ */ createDevtoolsComponentHook( + "component:removed" /* COMPONENT_REMOVED */ + ); + const devtoolsComponentRemoved = (component) => { + if (devtools$1 && typeof devtools$1.cleanupBuffer === "function" && // remove the component if it wasn't buffered + !devtools$1.cleanupBuffer(component)) { + _devtoolsComponentRemoved(component); + } + }; + function createDevtoolsComponentHook(hook) { + return (component) => { + emit$1( + hook, + component.appContext.app, + component.uid, + component.parent ? component.parent.uid : void 0, + component + ); + }; + } + const devtoolsPerfStart = /* @__PURE__ */ createDevtoolsPerformanceHook( + "perf:start" /* PERFORMANCE_START */ + ); + const devtoolsPerfEnd = /* @__PURE__ */ createDevtoolsPerformanceHook( + "perf:end" /* PERFORMANCE_END */ + ); + function createDevtoolsPerformanceHook(hook) { + return (component, type, time) => { + emit$1(hook, component.appContext.app, component.uid, component, type, time); + }; + } + function devtoolsComponentEmit(component, event, params) { + emit$1( + "component:emit" /* COMPONENT_EMIT */, + component.appContext.app, + component, + event, + params + ); + } + + function emit(instance, event, ...rawArgs) { + if (instance.isUnmounted) + return; + const props = instance.vnode.props || EMPTY_OBJ; + { + const { + emitsOptions, + propsOptions: [propsOptions] + } = instance; + if (emitsOptions) { + if (!(event in emitsOptions) && true) { + if (!propsOptions || !(toHandlerKey(event) in propsOptions)) { + warn$1( + `Component emitted event "${event}" but it is neither declared in the emits option nor as an "${toHandlerKey(event)}" prop.` + ); + } + } else { + const validator = emitsOptions[event]; + if (isFunction(validator)) { + const isValid = validator(...rawArgs); + if (!isValid) { + warn$1( + `Invalid event arguments: event validation failed for event "${event}".` + ); + } + } + } + } + } + let args = rawArgs; + const isModelListener = event.startsWith("update:"); + const modelArg = isModelListener && event.slice(7); + if (modelArg && modelArg in props) { + const modifiersKey = `${modelArg === "modelValue" ? "model" : modelArg}Modifiers`; + const { number, trim } = props[modifiersKey] || EMPTY_OBJ; + if (trim) { + args = rawArgs.map((a) => isString(a) ? a.trim() : a); + } + if (number) { + args = rawArgs.map(looseToNumber); + } + } + { + devtoolsComponentEmit(instance, event, args); + } + { + const lowerCaseEvent = event.toLowerCase(); + if (lowerCaseEvent !== event && props[toHandlerKey(lowerCaseEvent)]) { + warn$1( + `Event "${lowerCaseEvent}" is emitted in component ${formatComponentName( + instance, + instance.type + )} but the handler is registered for "${event}". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "${hyphenate( + event + )}" instead of "${event}".` + ); + } + } + let handlerName; + let handler = props[handlerName = toHandlerKey(event)] || // also try camelCase event handler (#2249) + props[handlerName = toHandlerKey(camelize(event))]; + if (!handler && isModelListener) { + handler = props[handlerName = toHandlerKey(hyphenate(event))]; + } + if (handler) { + callWithAsyncErrorHandling( + handler, + instance, + 6, + args + ); + } + const onceHandler = props[handlerName + `Once`]; + if (onceHandler) { + if (!instance.emitted) { + instance.emitted = {}; + } else if (instance.emitted[handlerName]) { + return; + } + instance.emitted[handlerName] = true; + callWithAsyncErrorHandling( + onceHandler, + instance, + 6, + args + ); + } + } + function normalizeEmitsOptions(comp, appContext, asMixin = false) { + const cache = appContext.emitsCache; + const cached = cache.get(comp); + if (cached !== void 0) { + return cached; + } + const raw = comp.emits; + let normalized = {}; + let hasExtends = false; + if (!isFunction(comp)) { + const extendEmits = (raw2) => { + const normalizedFromExtend = normalizeEmitsOptions(raw2, appContext, true); + if (normalizedFromExtend) { + hasExtends = true; + extend(normalized, normalizedFromExtend); + } + }; + if (!asMixin && appContext.mixins.length) { + appContext.mixins.forEach(extendEmits); + } + if (comp.extends) { + extendEmits(comp.extends); + } + if (comp.mixins) { + comp.mixins.forEach(extendEmits); + } + } + if (!raw && !hasExtends) { + if (isObject(comp)) { + cache.set(comp, null); + } + return null; + } + if (isArray(raw)) { + raw.forEach((key) => normalized[key] = null); + } else { + extend(normalized, raw); + } + if (isObject(comp)) { + cache.set(comp, normalized); + } + return normalized; + } + function isEmitListener(options, key) { + if (!options || !isOn(key)) { + return false; + } + key = key.slice(2).replace(/Once$/, ""); + return hasOwn(options, key[0].toLowerCase() + key.slice(1)) || hasOwn(options, hyphenate(key)) || hasOwn(options, key); + } + + let currentRenderingInstance = null; + let currentScopeId = null; + function setCurrentRenderingInstance(instance) { + const prev = currentRenderingInstance; + currentRenderingInstance = instance; + currentScopeId = instance && instance.type.__scopeId || null; + return prev; + } + function pushScopeId(id) { + currentScopeId = id; + } + function popScopeId() { + currentScopeId = null; + } + const withScopeId = (_id) => withCtx; + function withCtx(fn, ctx = currentRenderingInstance, isNonScopedSlot) { + if (!ctx) + return fn; + if (fn._n) { + return fn; + } + const renderFnWithContext = (...args) => { + if (renderFnWithContext._d) { + setBlockTracking(-1); + } + const prevInstance = setCurrentRenderingInstance(ctx); + let res; + try { + res = fn(...args); + } finally { + setCurrentRenderingInstance(prevInstance); + if (renderFnWithContext._d) { + setBlockTracking(1); + } + } + { + devtoolsComponentUpdated(ctx); + } + return res; + }; + renderFnWithContext._n = true; + renderFnWithContext._c = true; + renderFnWithContext._d = true; + return renderFnWithContext; + } + + let accessedAttrs = false; + function markAttrsAccessed() { + accessedAttrs = true; + } + function renderComponentRoot(instance) { + const { + type: Component, + vnode, + proxy, + withProxy, + props, + propsOptions: [propsOptions], + slots, + attrs, + emit, + render, + renderCache, + data, + setupState, + ctx, + inheritAttrs + } = instance; + let result; + let fallthroughAttrs; + const prev = setCurrentRenderingInstance(instance); + { + accessedAttrs = false; + } + try { + if (vnode.shapeFlag & 4) { + const proxyToUse = withProxy || proxy; + const thisProxy = setupState.__isScriptSetup ? new Proxy(proxyToUse, { + get(target, key, receiver) { + warn$1( + `Property '${String( + key + )}' was accessed via 'this'. Avoid using 'this' in templates.` + ); + return Reflect.get(target, key, receiver); + } + }) : proxyToUse; + result = normalizeVNode( + render.call( + thisProxy, + proxyToUse, + renderCache, + props, + setupState, + data, + ctx + ) + ); + fallthroughAttrs = attrs; + } else { + const render2 = Component; + if (attrs === props) { + markAttrsAccessed(); + } + result = normalizeVNode( + render2.length > 1 ? render2( + props, + true ? { + get attrs() { + markAttrsAccessed(); + return attrs; + }, + slots, + emit + } : { attrs, slots, emit } + ) : render2( + props, + null + /* we know it doesn't need it */ + ) + ); + fallthroughAttrs = Component.props ? attrs : getFunctionalFallthrough(attrs); + } + } catch (err) { + blockStack.length = 0; + handleError(err, instance, 1); + result = createVNode(Comment); + } + let root = result; + let setRoot = void 0; + if (result.patchFlag > 0 && result.patchFlag & 2048) { + [root, setRoot] = getChildRoot(result); + } + if (fallthroughAttrs && inheritAttrs !== false) { + const keys = Object.keys(fallthroughAttrs); + const { shapeFlag } = root; + if (keys.length) { + if (shapeFlag & (1 | 6)) { + if (propsOptions && keys.some(isModelListener)) { + fallthroughAttrs = filterModelListeners( + fallthroughAttrs, + propsOptions + ); + } + root = cloneVNode(root, fallthroughAttrs); + } else if (!accessedAttrs && root.type !== Comment) { + const allAttrs = Object.keys(attrs); + const eventAttrs = []; + const extraAttrs = []; + for (let i = 0, l = allAttrs.length; i < l; i++) { + const key = allAttrs[i]; + if (isOn(key)) { + if (!isModelListener(key)) { + eventAttrs.push(key[2].toLowerCase() + key.slice(3)); + } + } else { + extraAttrs.push(key); + } + } + if (extraAttrs.length) { + warn$1( + `Extraneous non-props attributes (${extraAttrs.join(", ")}) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.` + ); + } + if (eventAttrs.length) { + warn$1( + `Extraneous non-emits event listeners (${eventAttrs.join(", ")}) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.` + ); + } + } + } + } + if (vnode.dirs) { + if (!isElementRoot(root)) { + warn$1( + `Runtime directive used on component with non-element root node. The directives will not function as intended.` + ); + } + root = cloneVNode(root); + root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs; + } + if (vnode.transition) { + if (!isElementRoot(root)) { + warn$1( + `Component inside renders non-element root node that cannot be animated.` + ); + } + root.transition = vnode.transition; + } + if (setRoot) { + setRoot(root); + } else { + result = root; + } + setCurrentRenderingInstance(prev); + return result; + } + const getChildRoot = (vnode) => { + const rawChildren = vnode.children; + const dynamicChildren = vnode.dynamicChildren; + const childRoot = filterSingleRoot(rawChildren, false); + if (!childRoot) { + return [vnode, void 0]; + } else if (childRoot.patchFlag > 0 && childRoot.patchFlag & 2048) { + return getChildRoot(childRoot); + } + const index = rawChildren.indexOf(childRoot); + const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1; + const setRoot = (updatedRoot) => { + rawChildren[index] = updatedRoot; + if (dynamicChildren) { + if (dynamicIndex > -1) { + dynamicChildren[dynamicIndex] = updatedRoot; + } else if (updatedRoot.patchFlag > 0) { + vnode.dynamicChildren = [...dynamicChildren, updatedRoot]; + } + } + }; + return [normalizeVNode(childRoot), setRoot]; + }; + function filterSingleRoot(children, recurse = true) { + let singleRoot; + for (let i = 0; i < children.length; i++) { + const child = children[i]; + if (isVNode(child)) { + if (child.type !== Comment || child.children === "v-if") { + if (singleRoot) { + return; + } else { + singleRoot = child; + if (recurse && singleRoot.patchFlag > 0 && singleRoot.patchFlag & 2048) { + return filterSingleRoot(singleRoot.children); + } + } + } + } else { + return; + } + } + return singleRoot; + } + const getFunctionalFallthrough = (attrs) => { + let res; + for (const key in attrs) { + if (key === "class" || key === "style" || isOn(key)) { + (res || (res = {}))[key] = attrs[key]; + } + } + return res; + }; + const filterModelListeners = (attrs, props) => { + const res = {}; + for (const key in attrs) { + if (!isModelListener(key) || !(key.slice(9) in props)) { + res[key] = attrs[key]; + } + } + return res; + }; + const isElementRoot = (vnode) => { + return vnode.shapeFlag & (6 | 1) || vnode.type === Comment; + }; + function shouldUpdateComponent(prevVNode, nextVNode, optimized) { + const { props: prevProps, children: prevChildren, component } = prevVNode; + const { props: nextProps, children: nextChildren, patchFlag } = nextVNode; + const emits = component.emitsOptions; + if ((prevChildren || nextChildren) && isHmrUpdating) { + return true; + } + if (nextVNode.dirs || nextVNode.transition) { + return true; + } + if (optimized && patchFlag >= 0) { + if (patchFlag & 1024) { + return true; + } + if (patchFlag & 16) { + if (!prevProps) { + return !!nextProps; + } + return hasPropsChanged(prevProps, nextProps, emits); + } else if (patchFlag & 8) { + const dynamicProps = nextVNode.dynamicProps; + for (let i = 0; i < dynamicProps.length; i++) { + const key = dynamicProps[i]; + if (nextProps[key] !== prevProps[key] && !isEmitListener(emits, key)) { + return true; + } + } + } + } else { + if (prevChildren || nextChildren) { + if (!nextChildren || !nextChildren.$stable) { + return true; + } + } + if (prevProps === nextProps) { + return false; + } + if (!prevProps) { + return !!nextProps; + } + if (!nextProps) { + return true; + } + return hasPropsChanged(prevProps, nextProps, emits); + } + return false; + } + function hasPropsChanged(prevProps, nextProps, emitsOptions) { + const nextKeys = Object.keys(nextProps); + if (nextKeys.length !== Object.keys(prevProps).length) { + return true; + } + for (let i = 0; i < nextKeys.length; i++) { + const key = nextKeys[i]; + if (nextProps[key] !== prevProps[key] && !isEmitListener(emitsOptions, key)) { + return true; + } + } + return false; + } + function updateHOCHostEl({ vnode, parent }, el) { + while (parent) { + const root = parent.subTree; + if (root.suspense && root.suspense.activeBranch === vnode) { + root.el = vnode.el; + } + if (root === vnode) { + (vnode = parent.vnode).el = el; + parent = parent.parent; + } else { + break; + } + } + } + + const COMPONENTS = "components"; + const DIRECTIVES = "directives"; + function resolveComponent(name, maybeSelfReference) { + return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name; + } + const NULL_DYNAMIC_COMPONENT = Symbol.for("v-ndc"); + function resolveDynamicComponent(component) { + if (isString(component)) { + return resolveAsset(COMPONENTS, component, false) || component; + } else { + return component || NULL_DYNAMIC_COMPONENT; + } + } + function resolveDirective(name) { + return resolveAsset(DIRECTIVES, name); + } + function resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) { + const instance = currentRenderingInstance || currentInstance; + if (instance) { + const Component = instance.type; + if (type === COMPONENTS) { + const selfName = getComponentName( + Component, + false + ); + if (selfName && (selfName === name || selfName === camelize(name) || selfName === capitalize(camelize(name)))) { + return Component; + } + } + const res = ( + // local registration + // check instance[type] first which is resolved for options API + resolve(instance[type] || Component[type], name) || // global registration + resolve(instance.appContext[type], name) + ); + if (!res && maybeSelfReference) { + return Component; + } + if (warnMissing && !res) { + const extra = type === COMPONENTS ? ` +If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.` : ``; + warn$1(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`); + } + return res; + } else { + warn$1( + `resolve${capitalize(type.slice(0, -1))} can only be used in render() or setup().` + ); + } + } + function resolve(registry, name) { + return registry && (registry[name] || registry[camelize(name)] || registry[capitalize(camelize(name))]); + } + + const isSuspense = (type) => type.__isSuspense; + let suspenseId = 0; + const SuspenseImpl = { + name: "Suspense", + // In order to make Suspense tree-shakable, we need to avoid importing it + // directly in the renderer. The renderer checks for the __isSuspense flag + // on a vnode's type and calls the `process` method, passing in renderer + // internals. + __isSuspense: true, + process(n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals) { + if (n1 == null) { + mountSuspense( + n2, + container, + anchor, + parentComponent, + parentSuspense, + namespace, + slotScopeIds, + optimized, + rendererInternals + ); + } else { + if (parentSuspense && parentSuspense.deps > 0) { + n2.suspense = n1.suspense; + return; + } + patchSuspense( + n1, + n2, + container, + anchor, + parentComponent, + namespace, + slotScopeIds, + optimized, + rendererInternals + ); + } + }, + hydrate: hydrateSuspense, + create: createSuspenseBoundary, + normalize: normalizeSuspenseChildren + }; + const Suspense = SuspenseImpl ; + function triggerEvent(vnode, name) { + const eventListener = vnode.props && vnode.props[name]; + if (isFunction(eventListener)) { + eventListener(); + } + } + function mountSuspense(vnode, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals) { + const { + p: patch, + o: { createElement } + } = rendererInternals; + const hiddenContainer = createElement("div"); + const suspense = vnode.suspense = createSuspenseBoundary( + vnode, + parentSuspense, + parentComponent, + container, + hiddenContainer, + anchor, + namespace, + slotScopeIds, + optimized, + rendererInternals + ); + patch( + null, + suspense.pendingBranch = vnode.ssContent, + hiddenContainer, + null, + parentComponent, + suspense, + namespace, + slotScopeIds + ); + if (suspense.deps > 0) { + triggerEvent(vnode, "onPending"); + triggerEvent(vnode, "onFallback"); + patch( + null, + vnode.ssFallback, + container, + anchor, + parentComponent, + null, + // fallback tree will not have suspense context + namespace, + slotScopeIds + ); + setActiveBranch(suspense, vnode.ssFallback); + } else { + suspense.resolve(false, true); + } + } + function patchSuspense(n1, n2, container, anchor, parentComponent, namespace, slotScopeIds, optimized, { p: patch, um: unmount, o: { createElement } }) { + const suspense = n2.suspense = n1.suspense; + suspense.vnode = n2; + n2.el = n1.el; + const newBranch = n2.ssContent; + const newFallback = n2.ssFallback; + const { activeBranch, pendingBranch, isInFallback, isHydrating } = suspense; + if (pendingBranch) { + suspense.pendingBranch = newBranch; + if (isSameVNodeType(newBranch, pendingBranch)) { + patch( + pendingBranch, + newBranch, + suspense.hiddenContainer, + null, + parentComponent, + suspense, + namespace, + slotScopeIds, + optimized + ); + if (suspense.deps <= 0) { + suspense.resolve(); + } else if (isInFallback) { + if (!isHydrating) { + patch( + activeBranch, + newFallback, + container, + anchor, + parentComponent, + null, + // fallback tree will not have suspense context + namespace, + slotScopeIds, + optimized + ); + setActiveBranch(suspense, newFallback); + } + } + } else { + suspense.pendingId = suspenseId++; + if (isHydrating) { + suspense.isHydrating = false; + suspense.activeBranch = pendingBranch; + } else { + unmount(pendingBranch, parentComponent, suspense); + } + suspense.deps = 0; + suspense.effects.length = 0; + suspense.hiddenContainer = createElement("div"); + if (isInFallback) { + patch( + null, + newBranch, + suspense.hiddenContainer, + null, + parentComponent, + suspense, + namespace, + slotScopeIds, + optimized + ); + if (suspense.deps <= 0) { + suspense.resolve(); + } else { + patch( + activeBranch, + newFallback, + container, + anchor, + parentComponent, + null, + // fallback tree will not have suspense context + namespace, + slotScopeIds, + optimized + ); + setActiveBranch(suspense, newFallback); + } + } else if (activeBranch && isSameVNodeType(newBranch, activeBranch)) { + patch( + activeBranch, + newBranch, + container, + anchor, + parentComponent, + suspense, + namespace, + slotScopeIds, + optimized + ); + suspense.resolve(true); + } else { + patch( + null, + newBranch, + suspense.hiddenContainer, + null, + parentComponent, + suspense, + namespace, + slotScopeIds, + optimized + ); + if (suspense.deps <= 0) { + suspense.resolve(); + } + } + } + } else { + if (activeBranch && isSameVNodeType(newBranch, activeBranch)) { + patch( + activeBranch, + newBranch, + container, + anchor, + parentComponent, + suspense, + namespace, + slotScopeIds, + optimized + ); + setActiveBranch(suspense, newBranch); + } else { + triggerEvent(n2, "onPending"); + suspense.pendingBranch = newBranch; + if (newBranch.shapeFlag & 512) { + suspense.pendingId = newBranch.component.suspenseId; + } else { + suspense.pendingId = suspenseId++; + } + patch( + null, + newBranch, + suspense.hiddenContainer, + null, + parentComponent, + suspense, + namespace, + slotScopeIds, + optimized + ); + if (suspense.deps <= 0) { + suspense.resolve(); + } else { + const { timeout, pendingId } = suspense; + if (timeout > 0) { + setTimeout(() => { + if (suspense.pendingId === pendingId) { + suspense.fallback(newFallback); + } + }, timeout); + } else if (timeout === 0) { + suspense.fallback(newFallback); + } + } + } + } + } + let hasWarned = false; + function createSuspenseBoundary(vnode, parentSuspense, parentComponent, container, hiddenContainer, anchor, namespace, slotScopeIds, optimized, rendererInternals, isHydrating = false) { + if (!hasWarned) { + hasWarned = true; + console[console.info ? "info" : "log"]( + ` is an experimental feature and its API will likely change.` + ); + } + const { + p: patch, + m: move, + um: unmount, + n: next, + o: { parentNode, remove } + } = rendererInternals; + let parentSuspenseId; + const isSuspensible = isVNodeSuspensible(vnode); + if (isSuspensible) { + if (parentSuspense == null ? void 0 : parentSuspense.pendingBranch) { + parentSuspenseId = parentSuspense.pendingId; + parentSuspense.deps++; + } + } + const timeout = vnode.props ? toNumber(vnode.props.timeout) : void 0; + { + assertNumber(timeout, `Suspense timeout`); + } + const initialAnchor = anchor; + const suspense = { + vnode, + parent: parentSuspense, + parentComponent, + namespace, + container, + hiddenContainer, + deps: 0, + pendingId: suspenseId++, + timeout: typeof timeout === "number" ? timeout : -1, + activeBranch: null, + pendingBranch: null, + isInFallback: !isHydrating, + isHydrating, + isUnmounted: false, + effects: [], + resolve(resume = false, sync = false) { + { + if (!resume && !suspense.pendingBranch) { + throw new Error( + `suspense.resolve() is called without a pending branch.` + ); + } + if (suspense.isUnmounted) { + throw new Error( + `suspense.resolve() is called on an already unmounted suspense boundary.` + ); + } + } + const { + vnode: vnode2, + activeBranch, + pendingBranch, + pendingId, + effects, + parentComponent: parentComponent2, + container: container2 + } = suspense; + let delayEnter = false; + if (suspense.isHydrating) { + suspense.isHydrating = false; + } else if (!resume) { + delayEnter = activeBranch && pendingBranch.transition && pendingBranch.transition.mode === "out-in"; + if (delayEnter) { + activeBranch.transition.afterLeave = () => { + if (pendingId === suspense.pendingId) { + move( + pendingBranch, + container2, + anchor === initialAnchor ? next(activeBranch) : anchor, + 0 + ); + queuePostFlushCb(effects); + } + }; + } + if (activeBranch) { + if (parentNode(activeBranch.el) !== suspense.hiddenContainer) { + anchor = next(activeBranch); + } + unmount(activeBranch, parentComponent2, suspense, true); + } + if (!delayEnter) { + move(pendingBranch, container2, anchor, 0); + } + } + setActiveBranch(suspense, pendingBranch); + suspense.pendingBranch = null; + suspense.isInFallback = false; + let parent = suspense.parent; + let hasUnresolvedAncestor = false; + while (parent) { + if (parent.pendingBranch) { + parent.effects.push(...effects); + hasUnresolvedAncestor = true; + break; + } + parent = parent.parent; + } + if (!hasUnresolvedAncestor && !delayEnter) { + queuePostFlushCb(effects); + } + suspense.effects = []; + if (isSuspensible) { + if (parentSuspense && parentSuspense.pendingBranch && parentSuspenseId === parentSuspense.pendingId) { + parentSuspense.deps--; + if (parentSuspense.deps === 0 && !sync) { + parentSuspense.resolve(); + } + } + } + triggerEvent(vnode2, "onResolve"); + }, + fallback(fallbackVNode) { + if (!suspense.pendingBranch) { + return; + } + const { vnode: vnode2, activeBranch, parentComponent: parentComponent2, container: container2, namespace: namespace2 } = suspense; + triggerEvent(vnode2, "onFallback"); + const anchor2 = next(activeBranch); + const mountFallback = () => { + if (!suspense.isInFallback) { + return; + } + patch( + null, + fallbackVNode, + container2, + anchor2, + parentComponent2, + null, + // fallback tree will not have suspense context + namespace2, + slotScopeIds, + optimized + ); + setActiveBranch(suspense, fallbackVNode); + }; + const delayEnter = fallbackVNode.transition && fallbackVNode.transition.mode === "out-in"; + if (delayEnter) { + activeBranch.transition.afterLeave = mountFallback; + } + suspense.isInFallback = true; + unmount( + activeBranch, + parentComponent2, + null, + // no suspense so unmount hooks fire now + true + // shouldRemove + ); + if (!delayEnter) { + mountFallback(); + } + }, + move(container2, anchor2, type) { + suspense.activeBranch && move(suspense.activeBranch, container2, anchor2, type); + suspense.container = container2; + }, + next() { + return suspense.activeBranch && next(suspense.activeBranch); + }, + registerDep(instance, setupRenderEffect) { + const isInPendingSuspense = !!suspense.pendingBranch; + if (isInPendingSuspense) { + suspense.deps++; + } + const hydratedEl = instance.vnode.el; + instance.asyncDep.catch((err) => { + handleError(err, instance, 0); + }).then((asyncSetupResult) => { + if (instance.isUnmounted || suspense.isUnmounted || suspense.pendingId !== instance.suspenseId) { + return; + } + instance.asyncResolved = true; + const { vnode: vnode2 } = instance; + { + pushWarningContext(vnode2); + } + handleSetupResult(instance, asyncSetupResult, false); + if (hydratedEl) { + vnode2.el = hydratedEl; + } + const placeholder = !hydratedEl && instance.subTree.el; + setupRenderEffect( + instance, + vnode2, + // component may have been moved before resolve. + // if this is not a hydration, instance.subTree will be the comment + // placeholder. + parentNode(hydratedEl || instance.subTree.el), + // anchor will not be used if this is hydration, so only need to + // consider the comment placeholder case. + hydratedEl ? null : next(instance.subTree), + suspense, + namespace, + optimized + ); + if (placeholder) { + remove(placeholder); + } + updateHOCHostEl(instance, vnode2.el); + { + popWarningContext(); + } + if (isInPendingSuspense && --suspense.deps === 0) { + suspense.resolve(); + } + }); + }, + unmount(parentSuspense2, doRemove) { + suspense.isUnmounted = true; + if (suspense.activeBranch) { + unmount( + suspense.activeBranch, + parentComponent, + parentSuspense2, + doRemove + ); + } + if (suspense.pendingBranch) { + unmount( + suspense.pendingBranch, + parentComponent, + parentSuspense2, + doRemove + ); + } + } + }; + return suspense; + } + function hydrateSuspense(node, vnode, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals, hydrateNode) { + const suspense = vnode.suspense = createSuspenseBoundary( + vnode, + parentSuspense, + parentComponent, + node.parentNode, + // eslint-disable-next-line no-restricted-globals + document.createElement("div"), + null, + namespace, + slotScopeIds, + optimized, + rendererInternals, + true + ); + const result = hydrateNode( + node, + suspense.pendingBranch = vnode.ssContent, + parentComponent, + suspense, + slotScopeIds, + optimized + ); + if (suspense.deps === 0) { + suspense.resolve(false, true); + } + return result; + } + function normalizeSuspenseChildren(vnode) { + const { shapeFlag, children } = vnode; + const isSlotChildren = shapeFlag & 32; + vnode.ssContent = normalizeSuspenseSlot( + isSlotChildren ? children.default : children + ); + vnode.ssFallback = isSlotChildren ? normalizeSuspenseSlot(children.fallback) : createVNode(Comment); + } + function normalizeSuspenseSlot(s) { + let block; + if (isFunction(s)) { + const trackBlock = isBlockTreeEnabled && s._c; + if (trackBlock) { + s._d = false; + openBlock(); + } + s = s(); + if (trackBlock) { + s._d = true; + block = currentBlock; + closeBlock(); + } + } + if (isArray(s)) { + const singleChild = filterSingleRoot(s); + if (!singleChild && s.filter((child) => child !== NULL_DYNAMIC_COMPONENT).length > 0) { + warn$1(` slots expect a single root node.`); + } + s = singleChild; + } + s = normalizeVNode(s); + if (block && !s.dynamicChildren) { + s.dynamicChildren = block.filter((c) => c !== s); + } + return s; + } + function queueEffectWithSuspense(fn, suspense) { + if (suspense && suspense.pendingBranch) { + if (isArray(fn)) { + suspense.effects.push(...fn); + } else { + suspense.effects.push(fn); + } + } else { + queuePostFlushCb(fn); + } + } + function setActiveBranch(suspense, branch) { + suspense.activeBranch = branch; + const { vnode, parentComponent } = suspense; + let el = branch.el; + while (!el && branch.component) { + branch = branch.component.subTree; + el = branch.el; + } + vnode.el = el; + if (parentComponent && parentComponent.subTree === vnode) { + parentComponent.vnode.el = el; + updateHOCHostEl(parentComponent, el); + } + } + function isVNodeSuspensible(vnode) { + var _a; + return ((_a = vnode.props) == null ? void 0 : _a.suspensible) != null && vnode.props.suspensible !== false; + } + + const ssrContextKey = Symbol.for("v-scx"); + const useSSRContext = () => { + { + warn$1(`useSSRContext() is not supported in the global build.`); + } + }; + + function watchEffect(effect, options) { + return doWatch(effect, null, options); + } + function watchPostEffect(effect, options) { + return doWatch( + effect, + null, + extend({}, options, { flush: "post" }) + ); + } + function watchSyncEffect(effect, options) { + return doWatch( + effect, + null, + extend({}, options, { flush: "sync" }) + ); + } + const INITIAL_WATCHER_VALUE = {}; + function watch(source, cb, options) { + if (!isFunction(cb)) { + warn$1( + `\`watch(fn, options?)\` signature has been moved to a separate API. Use \`watchEffect(fn, options?)\` instead. \`watch\` now only supports \`watch(source, cb, options?) signature.` + ); + } + return doWatch(source, cb, options); + } + function doWatch(source, cb, { + immediate, + deep, + flush, + once, + onTrack, + onTrigger + } = EMPTY_OBJ) { + if (cb && once) { + const _cb = cb; + cb = (...args) => { + _cb(...args); + unwatch(); + }; + } + if (deep !== void 0 && typeof deep === "number") { + warn$1( + `watch() "deep" option with number value will be used as watch depth in future versions. Please use a boolean instead to avoid potential breakage.` + ); + } + if (!cb) { + if (immediate !== void 0) { + warn$1( + `watch() "immediate" option is only respected when using the watch(source, callback, options?) signature.` + ); + } + if (deep !== void 0) { + warn$1( + `watch() "deep" option is only respected when using the watch(source, callback, options?) signature.` + ); + } + if (once !== void 0) { + warn$1( + `watch() "once" option is only respected when using the watch(source, callback, options?) signature.` + ); + } + } + const warnInvalidSource = (s) => { + warn$1( + `Invalid watch source: `, + s, + `A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.` + ); + }; + const instance = currentInstance; + const reactiveGetter = (source2) => deep === true ? source2 : ( + // for deep: false, only traverse root-level properties + traverse(source2, deep === false ? 1 : void 0) + ); + let getter; + let forceTrigger = false; + let isMultiSource = false; + if (isRef(source)) { + getter = () => source.value; + forceTrigger = isShallow(source); + } else if (isReactive(source)) { + getter = () => reactiveGetter(source); + forceTrigger = true; + } else if (isArray(source)) { + isMultiSource = true; + forceTrigger = source.some((s) => isReactive(s) || isShallow(s)); + getter = () => source.map((s) => { + if (isRef(s)) { + return s.value; + } else if (isReactive(s)) { + return reactiveGetter(s); + } else if (isFunction(s)) { + return callWithErrorHandling(s, instance, 2); + } else { + warnInvalidSource(s); + } + }); + } else if (isFunction(source)) { + if (cb) { + getter = () => callWithErrorHandling(source, instance, 2); + } else { + getter = () => { + if (cleanup) { + cleanup(); + } + return callWithAsyncErrorHandling( + source, + instance, + 3, + [onCleanup] + ); + }; + } + } else { + getter = NOOP; + warnInvalidSource(source); + } + if (cb && deep) { + const baseGetter = getter; + getter = () => traverse(baseGetter()); + } + let cleanup; + let onCleanup = (fn) => { + cleanup = effect.onStop = () => { + callWithErrorHandling(fn, instance, 4); + cleanup = effect.onStop = void 0; + }; + }; + let oldValue = isMultiSource ? new Array(source.length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE; + const job = () => { + if (!effect.active || !effect.dirty) { + return; + } + if (cb) { + const newValue = effect.run(); + if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => hasChanged(v, oldValue[i])) : hasChanged(newValue, oldValue)) || false) { + if (cleanup) { + cleanup(); + } + callWithAsyncErrorHandling(cb, instance, 3, [ + newValue, + // pass undefined as the old value when it's changed for the first time + oldValue === INITIAL_WATCHER_VALUE ? void 0 : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue, + onCleanup + ]); + oldValue = newValue; + } + } else { + effect.run(); + } + }; + job.allowRecurse = !!cb; + let scheduler; + if (flush === "sync") { + scheduler = job; + } else if (flush === "post") { + scheduler = () => queuePostRenderEffect(job, instance && instance.suspense); + } else { + job.pre = true; + if (instance) + job.id = instance.uid; + scheduler = () => queueJob(job); + } + const effect = new ReactiveEffect(getter, NOOP, scheduler); + const scope = getCurrentScope(); + const unwatch = () => { + effect.stop(); + if (scope) { + remove(scope.effects, effect); + } + }; + { + effect.onTrack = onTrack; + effect.onTrigger = onTrigger; + } + if (cb) { + if (immediate) { + job(); + } else { + oldValue = effect.run(); + } + } else if (flush === "post") { + queuePostRenderEffect( + effect.run.bind(effect), + instance && instance.suspense + ); + } else { + effect.run(); + } + return unwatch; + } + function instanceWatch(source, value, options) { + const publicThis = this.proxy; + const getter = isString(source) ? source.includes(".") ? createPathGetter(publicThis, source) : () => publicThis[source] : source.bind(publicThis, publicThis); + let cb; + if (isFunction(value)) { + cb = value; + } else { + cb = value.handler; + options = value; + } + const reset = setCurrentInstance(this); + const res = doWatch(getter, cb.bind(publicThis), options); + reset(); + return res; + } + function createPathGetter(ctx, path) { + const segments = path.split("."); + return () => { + let cur = ctx; + for (let i = 0; i < segments.length && cur; i++) { + cur = cur[segments[i]]; + } + return cur; + }; + } + function traverse(value, depth, currentDepth = 0, seen) { + if (!isObject(value) || value["__v_skip"]) { + return value; + } + if (depth && depth > 0) { + if (currentDepth >= depth) { + return value; + } + currentDepth++; + } + seen = seen || /* @__PURE__ */ new Set(); + if (seen.has(value)) { + return value; + } + seen.add(value); + if (isRef(value)) { + traverse(value.value, depth, currentDepth, seen); + } else if (isArray(value)) { + for (let i = 0; i < value.length; i++) { + traverse(value[i], depth, currentDepth, seen); + } + } else if (isSet(value) || isMap(value)) { + value.forEach((v) => { + traverse(v, depth, currentDepth, seen); + }); + } else if (isPlainObject(value)) { + for (const key in value) { + traverse(value[key], depth, currentDepth, seen); + } + } + return value; + } + + function validateDirectiveName(name) { + if (isBuiltInDirective(name)) { + warn$1("Do not use built-in directive ids as custom directive id: " + name); + } + } + function withDirectives(vnode, directives) { + if (currentRenderingInstance === null) { + warn$1(`withDirectives can only be used inside render functions.`); + return vnode; + } + const instance = getExposeProxy(currentRenderingInstance) || currentRenderingInstance.proxy; + const bindings = vnode.dirs || (vnode.dirs = []); + for (let i = 0; i < directives.length; i++) { + let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i]; + if (dir) { + if (isFunction(dir)) { + dir = { + mounted: dir, + updated: dir + }; + } + if (dir.deep) { + traverse(value); + } + bindings.push({ + dir, + instance, + value, + oldValue: void 0, + arg, + modifiers + }); + } + } + return vnode; + } + function invokeDirectiveHook(vnode, prevVNode, instance, name) { + const bindings = vnode.dirs; + const oldBindings = prevVNode && prevVNode.dirs; + for (let i = 0; i < bindings.length; i++) { + const binding = bindings[i]; + if (oldBindings) { + binding.oldValue = oldBindings[i].value; + } + let hook = binding.dir[name]; + if (hook) { + pauseTracking(); + callWithAsyncErrorHandling(hook, instance, 8, [ + vnode.el, + binding, + vnode, + prevVNode + ]); + resetTracking(); + } + } + } + + const leaveCbKey = Symbol("_leaveCb"); + const enterCbKey$1 = Symbol("_enterCb"); + function useTransitionState() { + const state = { + isMounted: false, + isLeaving: false, + isUnmounting: false, + leavingVNodes: /* @__PURE__ */ new Map() + }; + onMounted(() => { + state.isMounted = true; + }); + onBeforeUnmount(() => { + state.isUnmounting = true; + }); + return state; + } + const TransitionHookValidator = [Function, Array]; + const BaseTransitionPropsValidators = { + mode: String, + appear: Boolean, + persisted: Boolean, + // enter + onBeforeEnter: TransitionHookValidator, + onEnter: TransitionHookValidator, + onAfterEnter: TransitionHookValidator, + onEnterCancelled: TransitionHookValidator, + // leave + onBeforeLeave: TransitionHookValidator, + onLeave: TransitionHookValidator, + onAfterLeave: TransitionHookValidator, + onLeaveCancelled: TransitionHookValidator, + // appear + onBeforeAppear: TransitionHookValidator, + onAppear: TransitionHookValidator, + onAfterAppear: TransitionHookValidator, + onAppearCancelled: TransitionHookValidator + }; + const BaseTransitionImpl = { + name: `BaseTransition`, + props: BaseTransitionPropsValidators, + setup(props, { slots }) { + const instance = getCurrentInstance(); + const state = useTransitionState(); + let prevTransitionKey; + return () => { + const children = slots.default && getTransitionRawChildren(slots.default(), true); + if (!children || !children.length) { + return; + } + let child = children[0]; + if (children.length > 1) { + let hasFound = false; + for (const c of children) { + if (c.type !== Comment) { + if (hasFound) { + warn$1( + " can only be used on a single element or component. Use for lists." + ); + break; + } + child = c; + hasFound = true; + } + } + } + const rawProps = toRaw(props); + const { mode } = rawProps; + if (mode && mode !== "in-out" && mode !== "out-in" && mode !== "default") { + warn$1(`invalid mode: ${mode}`); + } + if (state.isLeaving) { + return emptyPlaceholder(child); + } + const innerChild = getKeepAliveChild(child); + if (!innerChild) { + return emptyPlaceholder(child); + } + const enterHooks = resolveTransitionHooks( + innerChild, + rawProps, + state, + instance + ); + setTransitionHooks(innerChild, enterHooks); + const oldChild = instance.subTree; + const oldInnerChild = oldChild && getKeepAliveChild(oldChild); + let transitionKeyChanged = false; + const { getTransitionKey } = innerChild.type; + if (getTransitionKey) { + const key = getTransitionKey(); + if (prevTransitionKey === void 0) { + prevTransitionKey = key; + } else if (key !== prevTransitionKey) { + prevTransitionKey = key; + transitionKeyChanged = true; + } + } + if (oldInnerChild && oldInnerChild.type !== Comment && (!isSameVNodeType(innerChild, oldInnerChild) || transitionKeyChanged)) { + const leavingHooks = resolveTransitionHooks( + oldInnerChild, + rawProps, + state, + instance + ); + setTransitionHooks(oldInnerChild, leavingHooks); + if (mode === "out-in") { + state.isLeaving = true; + leavingHooks.afterLeave = () => { + state.isLeaving = false; + if (instance.update.active !== false) { + instance.effect.dirty = true; + instance.update(); + } + }; + return emptyPlaceholder(child); + } else if (mode === "in-out" && innerChild.type !== Comment) { + leavingHooks.delayLeave = (el, earlyRemove, delayedLeave) => { + const leavingVNodesCache = getLeavingNodesForType( + state, + oldInnerChild + ); + leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild; + el[leaveCbKey] = () => { + earlyRemove(); + el[leaveCbKey] = void 0; + delete enterHooks.delayedLeave; + }; + enterHooks.delayedLeave = delayedLeave; + }; + } + } + return child; + }; + } + }; + const BaseTransition = BaseTransitionImpl; + function getLeavingNodesForType(state, vnode) { + const { leavingVNodes } = state; + let leavingVNodesCache = leavingVNodes.get(vnode.type); + if (!leavingVNodesCache) { + leavingVNodesCache = /* @__PURE__ */ Object.create(null); + leavingVNodes.set(vnode.type, leavingVNodesCache); + } + return leavingVNodesCache; + } + function resolveTransitionHooks(vnode, props, state, instance) { + const { + appear, + mode, + persisted = false, + onBeforeEnter, + onEnter, + onAfterEnter, + onEnterCancelled, + onBeforeLeave, + onLeave, + onAfterLeave, + onLeaveCancelled, + onBeforeAppear, + onAppear, + onAfterAppear, + onAppearCancelled + } = props; + const key = String(vnode.key); + const leavingVNodesCache = getLeavingNodesForType(state, vnode); + const callHook = (hook, args) => { + hook && callWithAsyncErrorHandling( + hook, + instance, + 9, + args + ); + }; + const callAsyncHook = (hook, args) => { + const done = args[1]; + callHook(hook, args); + if (isArray(hook)) { + if (hook.every((hook2) => hook2.length <= 1)) + done(); + } else if (hook.length <= 1) { + done(); + } + }; + const hooks = { + mode, + persisted, + beforeEnter(el) { + let hook = onBeforeEnter; + if (!state.isMounted) { + if (appear) { + hook = onBeforeAppear || onBeforeEnter; + } else { + return; + } + } + if (el[leaveCbKey]) { + el[leaveCbKey]( + true + /* cancelled */ + ); + } + const leavingVNode = leavingVNodesCache[key]; + if (leavingVNode && isSameVNodeType(vnode, leavingVNode) && leavingVNode.el[leaveCbKey]) { + leavingVNode.el[leaveCbKey](); + } + callHook(hook, [el]); + }, + enter(el) { + let hook = onEnter; + let afterHook = onAfterEnter; + let cancelHook = onEnterCancelled; + if (!state.isMounted) { + if (appear) { + hook = onAppear || onEnter; + afterHook = onAfterAppear || onAfterEnter; + cancelHook = onAppearCancelled || onEnterCancelled; + } else { + return; + } + } + let called = false; + const done = el[enterCbKey$1] = (cancelled) => { + if (called) + return; + called = true; + if (cancelled) { + callHook(cancelHook, [el]); + } else { + callHook(afterHook, [el]); + } + if (hooks.delayedLeave) { + hooks.delayedLeave(); + } + el[enterCbKey$1] = void 0; + }; + if (hook) { + callAsyncHook(hook, [el, done]); + } else { + done(); + } + }, + leave(el, remove) { + const key2 = String(vnode.key); + if (el[enterCbKey$1]) { + el[enterCbKey$1]( + true + /* cancelled */ + ); + } + if (state.isUnmounting) { + return remove(); + } + callHook(onBeforeLeave, [el]); + let called = false; + const done = el[leaveCbKey] = (cancelled) => { + if (called) + return; + called = true; + remove(); + if (cancelled) { + callHook(onLeaveCancelled, [el]); + } else { + callHook(onAfterLeave, [el]); + } + el[leaveCbKey] = void 0; + if (leavingVNodesCache[key2] === vnode) { + delete leavingVNodesCache[key2]; + } + }; + leavingVNodesCache[key2] = vnode; + if (onLeave) { + callAsyncHook(onLeave, [el, done]); + } else { + done(); + } + }, + clone(vnode2) { + return resolveTransitionHooks(vnode2, props, state, instance); + } + }; + return hooks; + } + function emptyPlaceholder(vnode) { + if (isKeepAlive(vnode)) { + vnode = cloneVNode(vnode); + vnode.children = null; + return vnode; + } + } + function getKeepAliveChild(vnode) { + return isKeepAlive(vnode) ? ( + // #7121 ensure get the child component subtree in case + // it's been replaced during HMR + vnode.component ? vnode.component.subTree : vnode.children ? vnode.children[0] : void 0 + ) : vnode; + } + function setTransitionHooks(vnode, hooks) { + if (vnode.shapeFlag & 6 && vnode.component) { + setTransitionHooks(vnode.component.subTree, hooks); + } else if (vnode.shapeFlag & 128) { + vnode.ssContent.transition = hooks.clone(vnode.ssContent); + vnode.ssFallback.transition = hooks.clone(vnode.ssFallback); + } else { + vnode.transition = hooks; + } + } + function getTransitionRawChildren(children, keepComment = false, parentKey) { + let ret = []; + let keyedFragmentCount = 0; + for (let i = 0; i < children.length; i++) { + let child = children[i]; + const key = parentKey == null ? child.key : String(parentKey) + String(child.key != null ? child.key : i); + if (child.type === Fragment) { + if (child.patchFlag & 128) + keyedFragmentCount++; + ret = ret.concat( + getTransitionRawChildren(child.children, keepComment, key) + ); + } else if (keepComment || child.type !== Comment) { + ret.push(key != null ? cloneVNode(child, { key }) : child); + } + } + if (keyedFragmentCount > 1) { + for (let i = 0; i < ret.length; i++) { + ret[i].patchFlag = -2; + } + } + return ret; + } + + /*! #__NO_SIDE_EFFECTS__ */ + // @__NO_SIDE_EFFECTS__ + function defineComponent(options, extraOptions) { + return isFunction(options) ? ( + // #8326: extend call and options.name access are considered side-effects + // by Rollup, so we have to wrap it in a pure-annotated IIFE. + /* @__PURE__ */ (() => extend({ name: options.name }, extraOptions, { setup: options }))() + ) : options; + } + + const isAsyncWrapper = (i) => !!i.type.__asyncLoader; + /*! #__NO_SIDE_EFFECTS__ */ + // @__NO_SIDE_EFFECTS__ + function defineAsyncComponent(source) { + if (isFunction(source)) { + source = { loader: source }; + } + const { + loader, + loadingComponent, + errorComponent, + delay = 200, + timeout, + // undefined = never times out + suspensible = true, + onError: userOnError + } = source; + let pendingRequest = null; + let resolvedComp; + let retries = 0; + const retry = () => { + retries++; + pendingRequest = null; + return load(); + }; + const load = () => { + let thisRequest; + return pendingRequest || (thisRequest = pendingRequest = loader().catch((err) => { + err = err instanceof Error ? err : new Error(String(err)); + if (userOnError) { + return new Promise((resolve, reject) => { + const userRetry = () => resolve(retry()); + const userFail = () => reject(err); + userOnError(err, userRetry, userFail, retries + 1); + }); + } else { + throw err; + } + }).then((comp) => { + if (thisRequest !== pendingRequest && pendingRequest) { + return pendingRequest; + } + if (!comp) { + warn$1( + `Async component loader resolved to undefined. If you are using retry(), make sure to return its return value.` + ); + } + if (comp && (comp.__esModule || comp[Symbol.toStringTag] === "Module")) { + comp = comp.default; + } + if (comp && !isObject(comp) && !isFunction(comp)) { + throw new Error(`Invalid async component load result: ${comp}`); + } + resolvedComp = comp; + return comp; + })); + }; + return defineComponent({ + name: "AsyncComponentWrapper", + __asyncLoader: load, + get __asyncResolved() { + return resolvedComp; + }, + setup() { + const instance = currentInstance; + if (resolvedComp) { + return () => createInnerComp(resolvedComp, instance); + } + const onError = (err) => { + pendingRequest = null; + handleError( + err, + instance, + 13, + !errorComponent + ); + }; + if (suspensible && instance.suspense || false) { + return load().then((comp) => { + return () => createInnerComp(comp, instance); + }).catch((err) => { + onError(err); + return () => errorComponent ? createVNode(errorComponent, { + error: err + }) : null; + }); + } + const loaded = ref(false); + const error = ref(); + const delayed = ref(!!delay); + if (delay) { + setTimeout(() => { + delayed.value = false; + }, delay); + } + if (timeout != null) { + setTimeout(() => { + if (!loaded.value && !error.value) { + const err = new Error( + `Async component timed out after ${timeout}ms.` + ); + onError(err); + error.value = err; + } + }, timeout); + } + load().then(() => { + loaded.value = true; + if (instance.parent && isKeepAlive(instance.parent.vnode)) { + instance.parent.effect.dirty = true; + queueJob(instance.parent.update); + } + }).catch((err) => { + onError(err); + error.value = err; + }); + return () => { + if (loaded.value && resolvedComp) { + return createInnerComp(resolvedComp, instance); + } else if (error.value && errorComponent) { + return createVNode(errorComponent, { + error: error.value + }); + } else if (loadingComponent && !delayed.value) { + return createVNode(loadingComponent); + } + }; + } + }); + } + function createInnerComp(comp, parent) { + const { ref: ref2, props, children, ce } = parent.vnode; + const vnode = createVNode(comp, props, children); + vnode.ref = ref2; + vnode.ce = ce; + delete parent.vnode.ce; + return vnode; + } + + const isKeepAlive = (vnode) => vnode.type.__isKeepAlive; + const KeepAliveImpl = { + name: `KeepAlive`, + // Marker for special handling inside the renderer. We are not using a === + // check directly on KeepAlive in the renderer, because importing it directly + // would prevent it from being tree-shaken. + __isKeepAlive: true, + props: { + include: [String, RegExp, Array], + exclude: [String, RegExp, Array], + max: [String, Number] + }, + setup(props, { slots }) { + const instance = getCurrentInstance(); + const sharedContext = instance.ctx; + const cache = /* @__PURE__ */ new Map(); + const keys = /* @__PURE__ */ new Set(); + let current = null; + { + instance.__v_cache = cache; + } + const parentSuspense = instance.suspense; + const { + renderer: { + p: patch, + m: move, + um: _unmount, + o: { createElement } + } + } = sharedContext; + const storageContainer = createElement("div"); + sharedContext.activate = (vnode, container, anchor, namespace, optimized) => { + const instance2 = vnode.component; + move(vnode, container, anchor, 0, parentSuspense); + patch( + instance2.vnode, + vnode, + container, + anchor, + instance2, + parentSuspense, + namespace, + vnode.slotScopeIds, + optimized + ); + queuePostRenderEffect(() => { + instance2.isDeactivated = false; + if (instance2.a) { + invokeArrayFns(instance2.a); + } + const vnodeHook = vnode.props && vnode.props.onVnodeMounted; + if (vnodeHook) { + invokeVNodeHook(vnodeHook, instance2.parent, vnode); + } + }, parentSuspense); + { + devtoolsComponentAdded(instance2); + } + }; + sharedContext.deactivate = (vnode) => { + const instance2 = vnode.component; + move(vnode, storageContainer, null, 1, parentSuspense); + queuePostRenderEffect(() => { + if (instance2.da) { + invokeArrayFns(instance2.da); + } + const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted; + if (vnodeHook) { + invokeVNodeHook(vnodeHook, instance2.parent, vnode); + } + instance2.isDeactivated = true; + }, parentSuspense); + { + devtoolsComponentAdded(instance2); + } + }; + function unmount(vnode) { + resetShapeFlag(vnode); + _unmount(vnode, instance, parentSuspense, true); + } + function pruneCache(filter) { + cache.forEach((vnode, key) => { + const name = getComponentName(vnode.type); + if (name && (!filter || !filter(name))) { + pruneCacheEntry(key); + } + }); + } + function pruneCacheEntry(key) { + const cached = cache.get(key); + if (!current || !isSameVNodeType(cached, current)) { + unmount(cached); + } else if (current) { + resetShapeFlag(current); + } + cache.delete(key); + keys.delete(key); + } + watch( + () => [props.include, props.exclude], + ([include, exclude]) => { + include && pruneCache((name) => matches(include, name)); + exclude && pruneCache((name) => !matches(exclude, name)); + }, + // prune post-render after `current` has been updated + { flush: "post", deep: true } + ); + let pendingCacheKey = null; + const cacheSubtree = () => { + if (pendingCacheKey != null) { + cache.set(pendingCacheKey, getInnerChild(instance.subTree)); + } + }; + onMounted(cacheSubtree); + onUpdated(cacheSubtree); + onBeforeUnmount(() => { + cache.forEach((cached) => { + const { subTree, suspense } = instance; + const vnode = getInnerChild(subTree); + if (cached.type === vnode.type && cached.key === vnode.key) { + resetShapeFlag(vnode); + const da = vnode.component.da; + da && queuePostRenderEffect(da, suspense); + return; + } + unmount(cached); + }); + }); + return () => { + pendingCacheKey = null; + if (!slots.default) { + return null; + } + const children = slots.default(); + const rawVNode = children[0]; + if (children.length > 1) { + { + warn$1(`KeepAlive should contain exactly one component child.`); + } + current = null; + return children; + } else if (!isVNode(rawVNode) || !(rawVNode.shapeFlag & 4) && !(rawVNode.shapeFlag & 128)) { + current = null; + return rawVNode; + } + let vnode = getInnerChild(rawVNode); + const comp = vnode.type; + const name = getComponentName( + isAsyncWrapper(vnode) ? vnode.type.__asyncResolved || {} : comp + ); + const { include, exclude, max } = props; + if (include && (!name || !matches(include, name)) || exclude && name && matches(exclude, name)) { + current = vnode; + return rawVNode; + } + const key = vnode.key == null ? comp : vnode.key; + const cachedVNode = cache.get(key); + if (vnode.el) { + vnode = cloneVNode(vnode); + if (rawVNode.shapeFlag & 128) { + rawVNode.ssContent = vnode; + } + } + pendingCacheKey = key; + if (cachedVNode) { + vnode.el = cachedVNode.el; + vnode.component = cachedVNode.component; + if (vnode.transition) { + setTransitionHooks(vnode, vnode.transition); + } + vnode.shapeFlag |= 512; + keys.delete(key); + keys.add(key); + } else { + keys.add(key); + if (max && keys.size > parseInt(max, 10)) { + pruneCacheEntry(keys.values().next().value); + } + } + vnode.shapeFlag |= 256; + current = vnode; + return isSuspense(rawVNode.type) ? rawVNode : vnode; + }; + } + }; + const KeepAlive = KeepAliveImpl; + function matches(pattern, name) { + if (isArray(pattern)) { + return pattern.some((p) => matches(p, name)); + } else if (isString(pattern)) { + return pattern.split(",").includes(name); + } else if (isRegExp(pattern)) { + return pattern.test(name); + } + return false; + } + function onActivated(hook, target) { + registerKeepAliveHook(hook, "a", target); + } + function onDeactivated(hook, target) { + registerKeepAliveHook(hook, "da", target); + } + function registerKeepAliveHook(hook, type, target = currentInstance) { + const wrappedHook = hook.__wdc || (hook.__wdc = () => { + let current = target; + while (current) { + if (current.isDeactivated) { + return; + } + current = current.parent; + } + return hook(); + }); + injectHook(type, wrappedHook, target); + if (target) { + let current = target.parent; + while (current && current.parent) { + if (isKeepAlive(current.parent.vnode)) { + injectToKeepAliveRoot(wrappedHook, type, target, current); + } + current = current.parent; + } + } + } + function injectToKeepAliveRoot(hook, type, target, keepAliveRoot) { + const injected = injectHook( + type, + hook, + keepAliveRoot, + true + /* prepend */ + ); + onUnmounted(() => { + remove(keepAliveRoot[type], injected); + }, target); + } + function resetShapeFlag(vnode) { + vnode.shapeFlag &= ~256; + vnode.shapeFlag &= ~512; + } + function getInnerChild(vnode) { + return vnode.shapeFlag & 128 ? vnode.ssContent : vnode; + } + + function injectHook(type, hook, target = currentInstance, prepend = false) { + if (target) { + const hooks = target[type] || (target[type] = []); + const wrappedHook = hook.__weh || (hook.__weh = (...args) => { + if (target.isUnmounted) { + return; + } + pauseTracking(); + const reset = setCurrentInstance(target); + const res = callWithAsyncErrorHandling(hook, target, type, args); + reset(); + resetTracking(); + return res; + }); + if (prepend) { + hooks.unshift(wrappedHook); + } else { + hooks.push(wrappedHook); + } + return wrappedHook; + } else { + const apiName = toHandlerKey(ErrorTypeStrings$1[type].replace(/ hook$/, "")); + warn$1( + `${apiName} is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup().` + (` If you are using async setup(), make sure to register lifecycle hooks before the first await statement.` ) + ); + } + } + const createHook = (lifecycle) => (hook, target = currentInstance) => ( + // post-create lifecycle registrations are noops during SSR (except for serverPrefetch) + (!isInSSRComponentSetup || lifecycle === "sp") && injectHook(lifecycle, (...args) => hook(...args), target) + ); + const onBeforeMount = createHook("bm"); + const onMounted = createHook("m"); + const onBeforeUpdate = createHook("bu"); + const onUpdated = createHook("u"); + const onBeforeUnmount = createHook("bum"); + const onUnmounted = createHook("um"); + const onServerPrefetch = createHook("sp"); + const onRenderTriggered = createHook( + "rtg" + ); + const onRenderTracked = createHook( + "rtc" + ); + function onErrorCaptured(hook, target = currentInstance) { + injectHook("ec", hook, target); + } + + function renderList(source, renderItem, cache, index) { + let ret; + const cached = cache && cache[index]; + if (isArray(source) || isString(source)) { + ret = new Array(source.length); + for (let i = 0, l = source.length; i < l; i++) { + ret[i] = renderItem(source[i], i, void 0, cached && cached[i]); + } + } else if (typeof source === "number") { + if (!Number.isInteger(source)) { + warn$1(`The v-for range expect an integer value but got ${source}.`); + } + ret = new Array(source); + for (let i = 0; i < source; i++) { + ret[i] = renderItem(i + 1, i, void 0, cached && cached[i]); + } + } else if (isObject(source)) { + if (source[Symbol.iterator]) { + ret = Array.from( + source, + (item, i) => renderItem(item, i, void 0, cached && cached[i]) + ); + } else { + const keys = Object.keys(source); + ret = new Array(keys.length); + for (let i = 0, l = keys.length; i < l; i++) { + const key = keys[i]; + ret[i] = renderItem(source[key], key, i, cached && cached[i]); + } + } + } else { + ret = []; + } + if (cache) { + cache[index] = ret; + } + return ret; + } + + function createSlots(slots, dynamicSlots) { + for (let i = 0; i < dynamicSlots.length; i++) { + const slot = dynamicSlots[i]; + if (isArray(slot)) { + for (let j = 0; j < slot.length; j++) { + slots[slot[j].name] = slot[j].fn; + } + } else if (slot) { + slots[slot.name] = slot.key ? (...args) => { + const res = slot.fn(...args); + if (res) + res.key = slot.key; + return res; + } : slot.fn; + } + } + return slots; + } + + function renderSlot(slots, name, props = {}, fallback, noSlotted) { + if (currentRenderingInstance.isCE || currentRenderingInstance.parent && isAsyncWrapper(currentRenderingInstance.parent) && currentRenderingInstance.parent.isCE) { + if (name !== "default") + props.name = name; + return createVNode("slot", props, fallback && fallback()); + } + let slot = slots[name]; + if (slot && slot.length > 1) { + warn$1( + `SSR-optimized slot function detected in a non-SSR-optimized render function. You need to mark this component with $dynamic-slots in the parent template.` + ); + slot = () => []; + } + if (slot && slot._c) { + slot._d = false; + } + openBlock(); + const validSlotContent = slot && ensureValidVNode(slot(props)); + const rendered = createBlock( + Fragment, + { + key: props.key || // slot content array of a dynamic conditional slot may have a branch + // key attached in the `createSlots` helper, respect that + validSlotContent && validSlotContent.key || `_${name}` + }, + validSlotContent || (fallback ? fallback() : []), + validSlotContent && slots._ === 1 ? 64 : -2 + ); + if (!noSlotted && rendered.scopeId) { + rendered.slotScopeIds = [rendered.scopeId + "-s"]; + } + if (slot && slot._c) { + slot._d = true; + } + return rendered; + } + function ensureValidVNode(vnodes) { + return vnodes.some((child) => { + if (!isVNode(child)) + return true; + if (child.type === Comment) + return false; + if (child.type === Fragment && !ensureValidVNode(child.children)) + return false; + return true; + }) ? vnodes : null; + } + + function toHandlers(obj, preserveCaseIfNecessary) { + const ret = {}; + if (!isObject(obj)) { + warn$1(`v-on with no argument expects an object value.`); + return ret; + } + for (const key in obj) { + ret[preserveCaseIfNecessary && /[A-Z]/.test(key) ? `on:${key}` : toHandlerKey(key)] = obj[key]; + } + return ret; + } + + const getPublicInstance = (i) => { + if (!i) + return null; + if (isStatefulComponent(i)) + return getExposeProxy(i) || i.proxy; + return getPublicInstance(i.parent); + }; + const publicPropertiesMap = ( + // Move PURE marker to new line to workaround compiler discarding it + // due to type annotation + /* @__PURE__ */ extend(/* @__PURE__ */ Object.create(null), { + $: (i) => i, + $el: (i) => i.vnode.el, + $data: (i) => i.data, + $props: (i) => shallowReadonly(i.props) , + $attrs: (i) => shallowReadonly(i.attrs) , + $slots: (i) => shallowReadonly(i.slots) , + $refs: (i) => shallowReadonly(i.refs) , + $parent: (i) => getPublicInstance(i.parent), + $root: (i) => getPublicInstance(i.root), + $emit: (i) => i.emit, + $options: (i) => resolveMergedOptions(i) , + $forceUpdate: (i) => i.f || (i.f = () => { + i.effect.dirty = true; + queueJob(i.update); + }), + $nextTick: (i) => i.n || (i.n = nextTick.bind(i.proxy)), + $watch: (i) => instanceWatch.bind(i) + }) + ); + const isReservedPrefix = (key) => key === "_" || key === "$"; + const hasSetupBinding = (state, key) => state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn(state, key); + const PublicInstanceProxyHandlers = { + get({ _: instance }, key) { + const { ctx, setupState, data, props, accessCache, type, appContext } = instance; + if (key === "__isVue") { + return true; + } + let normalizedProps; + if (key[0] !== "$") { + const n = accessCache[key]; + if (n !== void 0) { + switch (n) { + case 1 /* SETUP */: + return setupState[key]; + case 2 /* DATA */: + return data[key]; + case 4 /* CONTEXT */: + return ctx[key]; + case 3 /* PROPS */: + return props[key]; + } + } else if (hasSetupBinding(setupState, key)) { + accessCache[key] = 1 /* SETUP */; + return setupState[key]; + } else if (data !== EMPTY_OBJ && hasOwn(data, key)) { + accessCache[key] = 2 /* DATA */; + return data[key]; + } else if ( + // only cache other properties when instance has declared (thus stable) + // props + (normalizedProps = instance.propsOptions[0]) && hasOwn(normalizedProps, key) + ) { + accessCache[key] = 3 /* PROPS */; + return props[key]; + } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { + accessCache[key] = 4 /* CONTEXT */; + return ctx[key]; + } else if (shouldCacheAccess) { + accessCache[key] = 0 /* OTHER */; + } + } + const publicGetter = publicPropertiesMap[key]; + let cssModule, globalProperties; + if (publicGetter) { + if (key === "$attrs") { + track(instance, "get", key); + markAttrsAccessed(); + } else if (key === "$slots") { + track(instance, "get", key); + } + return publicGetter(instance); + } else if ( + // css module (injected by vue-loader) + (cssModule = type.__cssModules) && (cssModule = cssModule[key]) + ) { + return cssModule; + } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) { + accessCache[key] = 4 /* CONTEXT */; + return ctx[key]; + } else if ( + // global properties + globalProperties = appContext.config.globalProperties, hasOwn(globalProperties, key) + ) { + { + return globalProperties[key]; + } + } else if (currentRenderingInstance && (!isString(key) || // #1091 avoid internal isRef/isVNode checks on component instance leading + // to infinite warning loop + key.indexOf("__v") !== 0)) { + if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn(data, key)) { + warn$1( + `Property ${JSON.stringify( + key + )} must be accessed via $data because it starts with a reserved character ("$" or "_") and is not proxied on the render context.` + ); + } else if (instance === currentRenderingInstance) { + warn$1( + `Property ${JSON.stringify(key)} was accessed during render but is not defined on instance.` + ); + } + } + }, + set({ _: instance }, key, value) { + const { data, setupState, ctx } = instance; + if (hasSetupBinding(setupState, key)) { + setupState[key] = value; + return true; + } else if (setupState.__isScriptSetup && hasOwn(setupState, key)) { + warn$1(`Cannot mutate