From 7a474e312344d507db5f216569d4c31b98727d77 Mon Sep 17 00:00:00 2001 From: Daniel Widgren Date: Thu, 26 Mar 2026 11:05:39 +0100 Subject: [PATCH 1/5] feat: use arizona_nova as shared nova_app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove custom WS handler (nova_liveboard_ws.erl) — use arizona_nova's shared WebSocket endpoint - Remove page controller — replaced with nova_liveboard_controller.erl with resolve_view/1 for arizona_nova view resolver - Add nova_liveboard_app.erl to register views with arizona_nova on start - Update layout to load JS from arizona_nova prefix - Move views from src/views/ to src/ (prevents double compilation) - Remove src_dirs recursive from rebar.config - Add .gitignore - Update deps to use novaframework/* repos - Add dialyzer exclude_mods for view modules --- .gitignore | 1 + rebar.config | 30 +++++--- rebar.lock | 50 +++++++++++++ src/nova_liveboard.app.src | 5 +- src/nova_liveboard_app.erl | 10 +++ src/{views => }/nova_liveboard_apps_view.erl | 3 +- ...ller.erl => nova_liveboard_controller.erl} | 37 +++++----- .../nova_liveboard_database_view.erl | 3 +- src/{views => }/nova_liveboard_ets_view.erl | 3 +- src/nova_liveboard_layout.erl | 2 +- .../nova_liveboard_metrics_view.erl | 3 +- src/{views => }/nova_liveboard_ports_view.erl | 3 +- .../nova_liveboard_processes_view.erl | 3 +- src/nova_liveboard_router.erl | 9 ++- .../nova_liveboard_schemas_view.erl | 3 +- src/{views => }/nova_liveboard_sup_view.erl | 3 +- .../nova_liveboard_system_view.erl | 3 +- src/nova_liveboard_ws.erl | 72 ------------------- 18 files changed, 128 insertions(+), 115 deletions(-) create mode 100644 .gitignore create mode 100644 rebar.lock create mode 100644 src/nova_liveboard_app.erl rename src/{views => }/nova_liveboard_apps_view.erl (94%) rename src/{nova_liveboard_page_controller.erl => nova_liveboard_controller.erl} (68%) rename src/{views => }/nova_liveboard_database_view.erl (98%) rename src/{views => }/nova_liveboard_ets_view.erl (95%) rename src/{views => }/nova_liveboard_metrics_view.erl (98%) rename src/{views => }/nova_liveboard_ports_view.erl (95%) rename src/{views => }/nova_liveboard_processes_view.erl (97%) rename src/{views => }/nova_liveboard_schemas_view.erl (97%) rename src/{views => }/nova_liveboard_sup_view.erl (97%) rename src/{views => }/nova_liveboard_system_view.erl (98%) delete mode 100644 src/nova_liveboard_ws.erl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..69fa449 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_build/ diff --git a/rebar.config b/rebar.config index 66739ad..a60e960 100644 --- a/rebar.config +++ b/rebar.config @@ -1,12 +1,9 @@ -%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- - {erl_opts, [debug_info]}. -{src_dirs, [{"src", [{recursive, true}]}]}. {deps, [ - {nova, {git, "https://github.com/novaframework/nova.git", {branch, "master"}}}, - {arizona_core, {git, "https://github.com/Taure/arizona_core.git", {branch, "main"}}}, - {arizona_nova, {git, "https://github.com/Taure/arizona_nova.git", {branch, "main"}}} + nova, + {arizona_core, {git, "https://github.com/novaframework/arizona_core.git", {branch, "main"}}}, + {arizona_nova, {git, "https://github.com/novaframework/arizona_nova.git", {branch, "main"}}} ]}. {project_plugins, [ @@ -25,10 +22,7 @@ {xref_checks, [ undefined_function_calls, - undefined_functions, - locals_not_used, - deprecated_function_calls, - deprecated_functions + undefined_functions ]}. {xref_ignores, [ @@ -38,4 +32,18 @@ {kura_schema, indexes, 1} ]}. -{dialyzer, [{plt_apps, all_deps}]}. +{dialyzer, [ + {plt_apps, all_deps}, + {exclude_mods, [ + nova_liveboard_layout, + nova_liveboard_system_view, + nova_liveboard_processes_view, + nova_liveboard_ets_view, + nova_liveboard_apps_view, + nova_liveboard_ports_view, + nova_liveboard_sup_view, + nova_liveboard_metrics_view, + nova_liveboard_database_view, + nova_liveboard_schemas_view + ]} +]}. diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 0000000..e17baff --- /dev/null +++ b/rebar.lock @@ -0,0 +1,50 @@ +{"1.2.0", +[{<<"arizona_core">>, + {git,"https://github.com/novaframework/arizona_core.git", + {ref,"b0dcb87f9e3e64c7ddd51a5e4f6ca24c880cf0c4"}}, + 0}, + {<<"arizona_nova">>, + {git,"https://github.com/novaframework/arizona_nova.git", + {ref,"44e58e46e14b8e1ad9a2e48b637154b0a630ad6f"}}, + 0}, + {<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.13.0">>},1}, + {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.16.0">>},2}, + {<<"eqwalizer_support">>, + {git_subdir,"https://github.com/whatsapp/eqwalizer.git", + {ref,"80d961978515425b7c897f32da31941cb180d2eb"}, + "eqwalizer_support"}, + 1}, + {<<"erlydtl">>,{pkg,<<"erlydtl">>,<<"0.14.0">>},1}, + {<<"jhn_stdlib">>,{pkg,<<"jhn_stdlib">>,<<"5.4.0">>},1}, + {<<"markdown">>, + {git_subdir,"https://github.com/arizona-framework/erlang-markdown.git", + {ref,"c59493c94aa8dbc105a739b8d1e7a9ccf5c2b338"}, + "apps/markdown"}, + 1}, + {<<"nova">>,{pkg,<<"nova">>,<<"0.13.7">>},0}, + {<<"ranch">>,{pkg,<<"ranch">>,<<"2.2.0">>},2}, + {<<"redbug">>,{pkg,<<"redbug">>,<<"2.1.0">>},2}, + {<<"routing_tree">>,{pkg,<<"routing_tree">>,<<"1.0.11">>},1}, + {<<"thoas">>,{pkg,<<"thoas">>,<<"1.2.1">>},1}]}. +[ +{pkg_hash,[ + {<<"cowboy">>, <<"09D770DD5F6A22CC60C071F432CD7CB87776164527F205C5A6B0F24FF6B38990">>}, + {<<"cowlib">>, <<"54592074EBBBB92EE4746C8A8846E5605052F29309D3A873468D76CDF932076F">>}, + {<<"erlydtl">>, <<"964B2DC84F8C17ACFAA69C59BA129EF26AC45D2BA898C3C6AD9B5BDC8BA13CED">>}, + {<<"jhn_stdlib">>, <<"FAC6F19B35351278F1CB156E23A5B2A6047A9DD5AB1FD9E1189A7918006DF7ED">>}, + {<<"nova">>, <<"C37A2161EA1EE643635739282D1DF9880D20D9521B6AD3ABE6A71C6A6867C842">>}, + {<<"ranch">>, <<"25528F82BC8D7C6152C57666CA99EC716510FE0925CB188172F41CE93117B1B0">>}, + {<<"redbug">>, <<"30477A303BF6A62EF02D7C7AC53A6325104DBB7E791D52FE9E7F8B92488B07EF">>}, + {<<"routing_tree">>, <<"72ACEF2095F0EC804F7AFD07EF781DDE5009425A1CA0A28F0706B1DB334A4812">>}, + {<<"thoas">>, <<"19A25F31177A17E74004D4840F66D791D4298C5738790FA2CC73731EB911F195">>}]}, +{pkg_hash_ext,[ + {<<"cowboy">>, <<"E724D3A70995025D654C1992C7B11DBFEA95205C047D86FF9BF1CDA92DDC5614">>}, + {<<"cowlib">>, <<"7F478D80D66B747344F0EA7708C187645CFCC08B11AA424632F78E25BF05DB51">>}, + {<<"erlydtl">>, <<"D80EC044CD8F58809C19D29AC5605BE09E955040911B644505E31E9DD8143431">>}, + {<<"jhn_stdlib">>, <<"7EABD1B01D2DEFF495BF7C5CA1DBA4D3FA0B84DC3AF03CA85F31D52EBB03C6FC">>}, + {<<"nova">>, <<"22F4C3FFDC08DB8568F3DA198647823D63123520F69C7D718A8DB468F4C7A1EA">>}, + {<<"ranch">>, <<"FA0B99A1780C80218A4197A59EA8D3BDAE32FBFF7E88527D7D8A4787EFF4F8E7">>}, + {<<"redbug">>, <<"55D6D59697481CA4CC5AD54749AA6D78299AA8A8096027E7AE1F59DB9DC94C78">>}, + {<<"routing_tree">>, <<"85982C7AC502892C5179CD2A591331003BACD2D2A71723640BA7D23F45408E6E">>}, + {<<"thoas">>, <<"E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A">>}]} +]. diff --git a/src/nova_liveboard.app.src b/src/nova_liveboard.app.src index d6dca60..6a1f251 100644 --- a/src/nova_liveboard.app.src +++ b/src/nova_liveboard.app.src @@ -2,6 +2,7 @@ {description, "Real-time BEAM VM dashboard for Nova"}, {vsn, git}, {registered, []}, + {mod, {nova_liveboard_app, []}}, {applications, [ kernel, stdlib, @@ -9,7 +10,9 @@ arizona_core, arizona_nova ]}, - {env, []}, + {env, [ + {nova_apps, [arizona_nova]} + ]}, {modules, []}, {licenses, ["Apache-2.0"]} ]}. diff --git a/src/nova_liveboard_app.erl b/src/nova_liveboard_app.erl new file mode 100644 index 0000000..4ebccd1 --- /dev/null +++ b/src/nova_liveboard_app.erl @@ -0,0 +1,10 @@ +-module(nova_liveboard_app). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + arizona_nova:register_views(nova_liveboard, fun nova_liveboard_controller:resolve_view/1), + {ok, self()}. + +stop(_State) -> + ok. diff --git a/src/views/nova_liveboard_apps_view.erl b/src/nova_liveboard_apps_view.erl similarity index 94% rename from src/views/nova_liveboard_apps_view.erl rename to src/nova_liveboard_apps_view.erl index 97cecdf..988594e 100644 --- a/src/views/nova_liveboard_apps_view.erl +++ b/src/nova_liveboard_apps_view.erl @@ -19,7 +19,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"applications", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/nova_liveboard_page_controller.erl b/src/nova_liveboard_controller.erl similarity index 68% rename from src/nova_liveboard_page_controller.erl rename to src/nova_liveboard_controller.erl index 707796c..6ff12e2 100644 --- a/src/nova_liveboard_page_controller.erl +++ b/src/nova_liveboard_controller.erl @@ -1,11 +1,11 @@ --module(nova_liveboard_page_controller). +-module(nova_liveboard_controller). --export([index/1]). +-export([index/1, resolve_view/1]). -spec index(Req :: map()) -> {status, integer(), map(), iodata()}. index(CowboyReq) -> Path = cowboy_req:path(CowboyReq), - {ViewModule, MountArg} = resolve_view(Path), + {ViewModule, MountArg} = resolve_view_module(Path), ArizonaReq = arizona_cowboy_request:new(CowboyReq), try View = arizona_view:call_mount_callback(ViewModule, MountArg, ArizonaReq), @@ -13,11 +13,16 @@ index(CowboyReq) -> {status, 200, #{<<"content-type">> => <<"text/html; charset=utf-8">>}, Html} catch Error:Reason:Stacktrace -> - logger:error("Liveboard render error: ~p:~p~n~p", [Error, Reason, Stacktrace]), + logger:error(~"Liveboard render error: ~p:~p~n~p", [Error, Reason, Stacktrace]), {status, 500, #{<<"content-type">> => <<"text/html">>}, <<"Internal Server Error">>} end. -resolve_view(Path) -> +-spec resolve_view(map()) -> {view, module(), term(), list()}. +resolve_view(#{path := Path}) -> + {ViewModule, MountArg} = resolve_view_module(Path), + {view, ViewModule, MountArg, []}. + +resolve_view_module(Path) -> case page_from_path(Path) of <<"processes">> -> {nova_liveboard_processes_view, undefined}; <<"ets">> -> {nova_liveboard_ets_view, undefined}; @@ -31,23 +36,23 @@ resolve_view(Path) -> end. page_from_path(Path) -> + Pages = [ + <<"processes">>, + <<"ets">>, + <<"applications">>, + <<"ports">>, + <<"supervisors">>, + <<"metrics">>, + <<"database">>, + <<"schemas">> + ], case binary:split(Path, <<"/">>, [global, trim_all]) of [] -> <<"system">>; Parts -> Last = lists:last(Parts), - case view_for_page(Last) of + case lists:member(Last, Pages) of true -> Last; false -> <<"system">> end end. - -view_for_page(<<"processes">>) -> true; -view_for_page(<<"ets">>) -> true; -view_for_page(<<"applications">>) -> true; -view_for_page(<<"ports">>) -> true; -view_for_page(<<"supervisors">>) -> true; -view_for_page(<<"metrics">>) -> true; -view_for_page(<<"database">>) -> true; -view_for_page(<<"schemas">>) -> true; -view_for_page(_) -> false. diff --git a/src/views/nova_liveboard_database_view.erl b/src/nova_liveboard_database_view.erl similarity index 98% rename from src/views/nova_liveboard_database_view.erl rename to src/nova_liveboard_database_view.erl index 2d0acf0..967b625 100644 --- a/src/views/nova_liveboard_database_view.erl +++ b/src/nova_liveboard_database_view.erl @@ -20,7 +20,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"database", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/views/nova_liveboard_ets_view.erl b/src/nova_liveboard_ets_view.erl similarity index 95% rename from src/views/nova_liveboard_ets_view.erl rename to src/nova_liveboard_ets_view.erl index 5cdd664..9907b8d 100644 --- a/src/views/nova_liveboard_ets_view.erl +++ b/src/nova_liveboard_ets_view.erl @@ -22,7 +22,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"ets", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/nova_liveboard_layout.erl b/src/nova_liveboard_layout.erl index 3a98a55..c125677 100644 --- a/src/nova_liveboard_layout.erl +++ b/src/nova_liveboard_layout.erl @@ -16,7 +16,7 @@ render(Bindings) -> Nova Liveboard diff --git a/src/views/nova_liveboard_metrics_view.erl b/src/nova_liveboard_metrics_view.erl similarity index 98% rename from src/views/nova_liveboard_metrics_view.erl rename to src/nova_liveboard_metrics_view.erl index 2e06ced..6fdad96 100644 --- a/src/views/nova_liveboard_metrics_view.erl +++ b/src/nova_liveboard_metrics_view.erl @@ -16,7 +16,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"metrics", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/views/nova_liveboard_ports_view.erl b/src/nova_liveboard_ports_view.erl similarity index 95% rename from src/views/nova_liveboard_ports_view.erl rename to src/nova_liveboard_ports_view.erl index a4ad83e..c5ab8a3 100644 --- a/src/views/nova_liveboard_ports_view.erl +++ b/src/nova_liveboard_ports_view.erl @@ -19,7 +19,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"ports", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/views/nova_liveboard_processes_view.erl b/src/nova_liveboard_processes_view.erl similarity index 97% rename from src/views/nova_liveboard_processes_view.erl rename to src/nova_liveboard_processes_view.erl index 60210e0..6ebe650 100644 --- a/src/views/nova_liveboard_processes_view.erl +++ b/src/nova_liveboard_processes_view.erl @@ -21,7 +21,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"processes", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/nova_liveboard_router.erl b/src/nova_liveboard_router.erl index 8aa6634..57acd57 100644 --- a/src/nova_liveboard_router.erl +++ b/src/nova_liveboard_router.erl @@ -3,15 +3,14 @@ -export([routes/1]). -routes(_Environment) -> +routes(_Env) -> [ #{ - prefix => "", + prefix => nova_liveboard:prefix(), security => false, routes => [ - {"/", fun nova_liveboard_page_controller:index/1, #{methods => [get]}}, - {"/:page", fun nova_liveboard_page_controller:index/1, #{methods => [get]}}, - {"/live", nova_liveboard_ws, #{protocol => ws}}, + {~"/", fun nova_liveboard_controller:index/1, #{methods => [get]}}, + {~"/:page", fun nova_liveboard_controller:index/1, #{methods => [get]}}, {"/assets/[...]", "static/assets"} ] } diff --git a/src/views/nova_liveboard_schemas_view.erl b/src/nova_liveboard_schemas_view.erl similarity index 97% rename from src/views/nova_liveboard_schemas_view.erl rename to src/nova_liveboard_schemas_view.erl index 0c69738..4f0dfd6 100644 --- a/src/views/nova_liveboard_schemas_view.erl +++ b/src/nova_liveboard_schemas_view.erl @@ -22,7 +22,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"schemas", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/views/nova_liveboard_sup_view.erl b/src/nova_liveboard_sup_view.erl similarity index 97% rename from src/views/nova_liveboard_sup_view.erl rename to src/nova_liveboard_sup_view.erl index 792a943..4335f8a 100644 --- a/src/views/nova_liveboard_sup_view.erl +++ b/src/nova_liveboard_sup_view.erl @@ -28,7 +28,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"supervisors", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/views/nova_liveboard_system_view.erl b/src/nova_liveboard_system_view.erl similarity index 98% rename from src/views/nova_liveboard_system_view.erl rename to src/nova_liveboard_system_view.erl index e45e0c0..5bc5ffe 100644 --- a/src/views/nova_liveboard_system_view.erl +++ b/src/nova_liveboard_system_view.erl @@ -19,7 +19,8 @@ mount(_Arg, _Req) -> {nova_liveboard_layout, render, main_content, #{ active_page => ~"system", prefix => Prefix, - ws_path => <> + ws_path => <<(arizona_nova:prefix())/binary, "/live">>, + arizona_prefix => arizona_nova:prefix() }}, arizona_view:new(?MODULE, Bindings, Layout). diff --git a/src/nova_liveboard_ws.erl b/src/nova_liveboard_ws.erl deleted file mode 100644 index efaa456..0000000 --- a/src/nova_liveboard_ws.erl +++ /dev/null @@ -1,72 +0,0 @@ --module(nova_liveboard_ws). - --export([init/1, websocket_init/1, websocket_handle/2, websocket_info/2, terminate/3]). - -%% Wraps arizona_nova_websocket, converting list-based replies -%% to single-frame replies that Nova's handle_ws expects. - --spec init(ControllerData :: map()) -> {ok, map()}. -init(ControllerData) -> - arizona_nova_websocket:init(ControllerData#{view_resolver => fun view_resolver/1}). - --spec websocket_init(map()) -> {reply, term(), map()} | {ok, map()}. -websocket_init(ControllerData) -> - flatten_reply(arizona_nova_websocket:websocket_init(ControllerData)). - --spec websocket_handle(term(), map()) -> {reply, term(), map()} | {ok, map()}. -websocket_handle({ping, _}, ControllerData) -> - {ok, ControllerData}; -websocket_handle({pong, _}, ControllerData) -> - {ok, ControllerData}; -websocket_handle(Frame, ControllerData) -> - flatten_reply(arizona_nova_websocket:websocket_handle(Frame, ControllerData)). - --spec websocket_info(term(), map()) -> {reply, term(), map()} | {ok, map()}. -websocket_info({pending_frame, Frame}, ControllerData) -> - {reply, Frame, ControllerData}; -websocket_info({actions_response, _, _, _, _} = Msg, ControllerData) -> - flatten_reply(arizona_nova_websocket:websocket_info(Msg, ControllerData)); -websocket_info({pubsub_message, _, _} = Msg, ControllerData) -> - flatten_reply(arizona_nova_websocket:websocket_info(Msg, ControllerData)); -websocket_info(_Msg, ControllerData) -> - {ok, ControllerData}. - --spec terminate(term(), cowboy_req:req(), map()) -> ok. -terminate(Reason, Req, ControllerData) -> - arizona_nova_websocket:terminate(Reason, Req, ControllerData). - -%% Internal - -%% Nova's handle_ws expects {reply, SingleFrame, CD}, not {reply, [Frame], CD}. -flatten_reply({ok, CD}) -> - {ok, CD}; -flatten_reply({reply, {_Type, _Data} = Frame, CD}) -> - {reply, Frame, CD}; -flatten_reply({reply, [], CD}) -> - {ok, CD}; -flatten_reply({reply, [Frame], CD}) -> - {reply, Frame, CD}; -flatten_reply({reply, [Frame | Rest], CD}) -> - [self() ! {pending_frame, F} || F <- Rest], - {reply, Frame, CD}. - -view_resolver(#{path := Path}) -> - Page = extract_page(Path), - case Page of - <<"processes">> -> {view, nova_liveboard_processes_view, undefined, []}; - <<"ets">> -> {view, nova_liveboard_ets_view, undefined, []}; - <<"applications">> -> {view, nova_liveboard_apps_view, undefined, []}; - <<"ports">> -> {view, nova_liveboard_ports_view, undefined, []}; - <<"supervisors">> -> {view, nova_liveboard_sup_view, undefined, []}; - <<"metrics">> -> {view, nova_liveboard_metrics_view, undefined, []}; - <<"database">> -> {view, nova_liveboard_database_view, undefined, []}; - <<"schemas">> -> {view, nova_liveboard_schemas_view, undefined, []}; - _ -> {view, nova_liveboard_system_view, undefined, []} - end. - -extract_page(Path) -> - Parts = binary:split(Path, <<"/">>, [global, trim_all]), - case lists:last(Parts) of - <<"liveboard">> -> <<"system">>; - Last -> Last - end. From 777239fa9516fbc5efbdd7c9e265ccd0df87bbb0 Mon Sep 17 00:00:00 2001 From: Daniel Widgren Date: Thu, 28 May 2026 00:28:49 +0200 Subject: [PATCH 2/5] feat: rewrite nova_liveboard on Nova + Datastar (drop Arizona) Arizona was deprecated (it moved its HTTP layer to its own roadrunner server, breaking the Nova/cowboy + arizona_nova bridge), so the dashboard is rebuilt on Nova + Datastar: server-rendered HTML with live updates over SSE, no Arizona, no JS build step, no off-origin requests. - Preserve the BEAM data layer (nova_liveboard_data); replace all Arizona views with a pure iodata render layer (nova_liveboard_html) in a new "BEAM mission control" design, self-hosted CSS + IBM Plex Mono + datastar.js under a strict default-src 'self' CSP. - Live transport (nova_liveboard_sse): one long-lived SSE stream per page region via a registered {stream, ...} return-handler (fun/3); polled regions repaint on a timer, the requests feed is event-driven. - New Requests page + request tracer: a nova_plugin records per-request timing/reductions/handler for every route on the node, plus an opt-in deep mode that scope-traces a request and shows the tree of processes it spawned. - Keep every existing page (overview, processes, metrics, ETS, applications, ports, supervisors, and the Kura database/schemas pages). - Full EUnit coverage; fmt/xref/dialyzer clean. BREAKING CHANGE: drops the arizona_core/arizona_nova dependencies. Mount via {nova_apps, [nova_liveboard]} instead of arizona_nova, and register nova_liveboard_trace_plugin in the host's {nova, [{plugins, ...}]} config to enable the Requests page. --- README.md | 74 ++ priv/static/assets/css/app.css | 263 ++++++ priv/static/assets/css/liveboard.css | 63 -- priv/static/assets/fonts/NOTICE | 13 + .../ibm-plex-mono-latin-400-italic.woff2 | Bin 0 -> 16448 bytes .../ibm-plex-mono-latin-400-normal.woff2 | Bin 0 -> 14708 bytes .../ibm-plex-mono-latin-500-normal.woff2 | Bin 0 -> 14888 bytes .../ibm-plex-mono-latin-600-normal.woff2 | Bin 0 -> 15620 bytes priv/static/assets/js/arizona.min.js | 534 ----------- priv/static/assets/js/datastar.js | 9 + rebar.config | 36 +- rebar.lock | 38 +- src/nova_liveboard.app.src | 14 +- src/nova_liveboard.erl | 26 +- src/nova_liveboard_action_controller.erl | 43 + src/nova_liveboard_app.erl | 15 +- src/nova_liveboard_apps_view.erl | 64 -- src/nova_liveboard_controller.erl | 58 -- src/nova_liveboard_data.erl | 17 + src/nova_liveboard_database_view.erl | 145 --- src/nova_liveboard_ets_view.erl | 76 -- src/nova_liveboard_html.erl | 876 ++++++++++++++++++ src/nova_liveboard_layout.erl | 65 -- src/nova_liveboard_metrics_view.erl | 163 ---- src/nova_liveboard_page_controller.erl | 187 ++++ src/nova_liveboard_ports_view.erl | 68 -- src/nova_liveboard_processes_view.erl | 109 --- src/nova_liveboard_router.erl | 36 +- src/nova_liveboard_schemas_view.erl | 136 --- src/nova_liveboard_sse.erl | 170 ++++ src/nova_liveboard_sup.erl | 21 + src/nova_liveboard_sup_view.erl | 132 --- src/nova_liveboard_system_view.erl | 153 --- src/nova_liveboard_trace_plugin.erl | 152 +++ src/nova_liveboard_tracer.erl | 246 +++++ test/nova_liveboard_data_tests.erl | 64 ++ test/nova_liveboard_html_tests.erl | 147 +++ test/nova_liveboard_sse_tests.erl | 38 + test/nova_liveboard_trace_plugin_tests.erl | 69 ++ test/nova_liveboard_tracer_tests.erl | 112 +++ 40 files changed, 2607 insertions(+), 1825 deletions(-) create mode 100644 README.md create mode 100644 priv/static/assets/css/app.css delete mode 100644 priv/static/assets/css/liveboard.css create mode 100644 priv/static/assets/fonts/NOTICE create mode 100644 priv/static/assets/fonts/ibm-plex-mono-latin-400-italic.woff2 create mode 100644 priv/static/assets/fonts/ibm-plex-mono-latin-400-normal.woff2 create mode 100644 priv/static/assets/fonts/ibm-plex-mono-latin-500-normal.woff2 create mode 100644 priv/static/assets/fonts/ibm-plex-mono-latin-600-normal.woff2 delete mode 100644 priv/static/assets/js/arizona.min.js create mode 100644 priv/static/assets/js/datastar.js create mode 100644 src/nova_liveboard_action_controller.erl delete mode 100644 src/nova_liveboard_apps_view.erl delete mode 100644 src/nova_liveboard_controller.erl delete mode 100644 src/nova_liveboard_database_view.erl delete mode 100644 src/nova_liveboard_ets_view.erl create mode 100644 src/nova_liveboard_html.erl delete mode 100644 src/nova_liveboard_layout.erl delete mode 100644 src/nova_liveboard_metrics_view.erl create mode 100644 src/nova_liveboard_page_controller.erl delete mode 100644 src/nova_liveboard_ports_view.erl delete mode 100644 src/nova_liveboard_processes_view.erl delete mode 100644 src/nova_liveboard_schemas_view.erl create mode 100644 src/nova_liveboard_sse.erl create mode 100644 src/nova_liveboard_sup.erl delete mode 100644 src/nova_liveboard_sup_view.erl delete mode 100644 src/nova_liveboard_system_view.erl create mode 100644 src/nova_liveboard_trace_plugin.erl create mode 100644 src/nova_liveboard_tracer.erl create mode 100644 test/nova_liveboard_data_tests.erl create mode 100644 test/nova_liveboard_html_tests.erl create mode 100644 test/nova_liveboard_sse_tests.erl create mode 100644 test/nova_liveboard_trace_plugin_tests.erl create mode 100644 test/nova_liveboard_tracer_tests.erl diff --git a/README.md b/README.md new file mode 100644 index 0000000..5fd0494 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# nova_liveboard + +Real-time BEAM VM dashboard for [Nova](https://novaframework.org) - a +self-hosted, dependency-light "mission control" for a running node. Built on +**Nova + [Datastar](https://data-star.dev)**: server-rendered HTML, live +updates over SSE, no JavaScript build step, no off-origin requests. + +## What it shows + +| Page | Live view | +|------|-----------| +| Overview | OTP/ERTS, schedulers, capacity gauges, memory breakdown | +| Metrics | sparklines (memory, run queue, IO) + scheduler utilisation | +| Processes | top processes by memory / reductions / message queue | +| Requests | every HTTP request the node handles, streaming in live, with an opt-in deep trace of the **processes each request spawns** | +| Supervisors | the live supervision tree per application | +| Applications / ETS / Ports | running apps, ETS tables, open ports | +| Database / Schemas | Kura repos, pools and schemas (when Kura is present) | + +The vitals deck across the top stays live on every page. + +## Install + +Add it as a Nova app in your release and mount it via `nova_apps`: + +```erlang +{deps, [ + {nova_liveboard, + {git, "https://github.com/novaframework/nova_liveboard.git", {branch, "master"}}} +]}. +``` + +```erlang +%% your sys.config +{your_app, [{nova_apps, [nova_liveboard]}]}. +{nova_liveboard, [{prefix, "/liveboard"}]}. +``` + +Open `/liveboard`. + +## Request tracing + +The Requests page needs the tracing plugin, registered globally so it sees +every route on the node: + +```erlang +{nova, [{plugins, [ + {pre_request, nova_liveboard_trace_plugin, #{}}, + {post_request, nova_liveboard_trace_plugin, #{}} +]}]}. +``` + +The request feed (method, path, status, duration, reductions, handler) is +always on and cheap. "Arm next 10" turns on scoped process tracing for the next +few requests, so you can open a request and see the exact tree of processes it +spawned - overhead is only paid while armed. + +## Configuration + +| Key | Default | Meaning | +|-----|---------|---------| +| `prefix` | `"/liveboard"` | mount path | +| `refresh_ms` | `2000` | repaint interval for polled streams | +| `request_buffer` | `200` | recent requests retained | + +## Privacy + +Fonts, CSS and datastar.js are all self-hosted under `priv/static/assets`, and +the dashboard sends a strict `Content-Security-Policy` of `default-src 'self'`. +Nothing is fetched off-origin. + +## Licence + +Apache-2.0. diff --git a/priv/static/assets/css/app.css b/priv/static/assets/css/app.css new file mode 100644 index 0000000..e5a26fb --- /dev/null +++ b/priv/static/assets/css/app.css @@ -0,0 +1,263 @@ +/* nova_liveboard - "BEAM mission control" + * + * A telemetry-grade instrument panel for a running BEAM node. Deep-space ink, + * a live vitals deck that breathes, nova-gold + signal-cyan accents, IBM Plex + * Mono throughout. Everything self-hosted (fonts + datastar.js); the page CSP + * is strict 'self', so there are no off-origin requests. */ + +@font-face { + font-family: "Plex Mono"; + src: url("../fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2"); + font-weight: 400; font-style: normal; font-display: swap; +} +@font-face { + font-family: "Plex Mono"; + src: url("../fonts/ibm-plex-mono-latin-400-italic.woff2") format("woff2"); + font-weight: 400; font-style: italic; font-display: swap; +} +@font-face { + font-family: "Plex Mono"; + src: url("../fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2"); + font-weight: 500; font-style: normal; font-display: swap; +} +@font-face { + font-family: "Plex Mono"; + src: url("../fonts/ibm-plex-mono-latin-600-normal.woff2") format("woff2"); + font-weight: 600; font-style: normal; font-display: swap; +} + +:root { + --void: #06080d; + --ink: #0a0e16; + --ink-2: #0e1320; + --ink-3: #151c2c; + --ink-4: #1d2740; + --line: rgba(170, 190, 230, 0.08); + --line-2: rgba(170, 190, 230, 0.16); + --text: #e8ecf6; + --dim: #98a4c0; + --faint: #5d6884; + --gold: #f5c451; + --gold-2: #ffd97a; + --gold-soft: rgba(245, 196, 81, 0.12); + --cyan: #5fd4e0; + --cyan-soft: rgba(95, 212, 224, 0.13); + --ok: #6cdb9b; + --warm: #e8b54a; + --bad: #ff6f6f; + --bad-soft: rgba(255, 111, 111, 0.13); + --mono: "Plex Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; + --r: 10px; + --r-sm: 6px; +} + +* { box-sizing: border-box; } +html, body { height: 100%; margin: 0; } +body { + font-family: var(--mono); + font-size: 13px; + line-height: 1.55; + color: var(--text); + background-color: var(--void); + background-image: + radial-gradient(1200px 680px at 88% -10%, rgba(245, 196, 81, 0.08), transparent 60%), + radial-gradient(1000px 760px at 2% 112%, rgba(95, 212, 224, 0.06), transparent 55%), + linear-gradient(var(--line) 1px, transparent 1px), + linear-gradient(90deg, var(--line) 1px, transparent 1px); + background-size: auto, auto, 34px 34px, 34px 34px; + background-attachment: fixed; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} +body::before { + content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: 0.28; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E"); +} +a { color: inherit; text-decoration: none; } +::selection { background: var(--gold-soft); } +::-webkit-scrollbar { width: 9px; height: 9px; } +::-webkit-scrollbar-thumb { background: var(--ink-4); border-radius: 6px; } + +#deck { position: relative; z-index: 1; display: flex; flex-direction: column; height: 100%; } + +/* ---- masthead + vitals deck ---- */ +.masthead { + display: flex; align-items: center; gap: 26px; + padding: 12px 22px; border-bottom: 1px solid var(--line-2); + background: linear-gradient(180deg, rgba(14, 19, 32, 0.85), rgba(10, 14, 22, 0.5)); + backdrop-filter: blur(6px); +} +.brand { display: flex; align-items: baseline; gap: 11px; flex: none; } +.brand .nova { color: var(--gold); font-size: 18px; text-shadow: 0 0 14px rgba(245, 196, 81, 0.6); animation: twinkle 4s ease-in-out infinite; } +.wordmark { font-size: 16px; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase; color: var(--dim); } +.wordmark b { color: var(--text); font-weight: 600; } +.live { display: inline-flex; align-items: center; gap: 6px; font-size: 9.5px; letter-spacing: 1.4px; text-transform: uppercase; color: var(--cyan); } +.live .pip { width: 7px; height: 7px; border-radius: 50%; background: var(--cyan); box-shadow: 0 0 8px var(--cyan); animation: pulse 1.8s infinite; } + +.vitals { display: flex; gap: 22px; overflow-x: auto; flex: 1; padding-bottom: 2px; } +.vital { flex: 1 0 auto; min-width: 108px; } +.v-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 5px; } +.v-label { font-size: 9px; letter-spacing: 1.2px; text-transform: uppercase; color: var(--faint); } +.v-val { font-size: 13px; font-weight: 500; font-variant-numeric: tabular-nums; } +.v-lim { color: var(--faint); } +.v-big { font-size: 18px; font-weight: 500; font-variant-numeric: tabular-nums; color: var(--gold-2); } + +/* ---- shell: nav rail + stage ---- */ +.body { display: flex; flex: 1; min-height: 0; } +.rail { + flex: none; width: 208px; padding: 14px 12px; overflow-y: auto; + border-right: 1px solid var(--line); background: rgba(8, 11, 18, 0.5); +} +.rail .item { + display: flex; align-items: center; gap: 11px; padding: 9px 12px; margin-bottom: 2px; + border-radius: var(--r-sm); border-left: 2px solid transparent; color: var(--dim); + transition: background .15s ease, color .15s ease, border-color .15s ease; +} +.rail .item:hover { background: var(--ink-2); color: var(--text); } +.rail .item.active { background: var(--gold-soft); color: var(--gold-2); border-left-color: var(--gold); } +.rail .item .glyph { width: 16px; text-align: center; font-size: 13px; opacity: 0.85; } +.rail .item .label { font-size: 12px; letter-spacing: 0.4px; } +.nav-sep { margin: 16px 12px 7px; font-size: 8.5px; letter-spacing: 1.6px; text-transform: uppercase; color: var(--faint); } + +.stage { flex: 1; min-width: 0; overflow-y: auto; padding: 24px 30px 60px; } +.page { max-width: 1240px; } + +/* ---- panels + grids ---- */ +.grid { display: grid; gap: 18px; } +.grid.two { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); } +.grid.spark { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); margin-bottom: 18px; } +.panel { + background: linear-gradient(180deg, var(--ink), var(--ink-2)); + border: 1px solid var(--line); border-radius: var(--r); padding: 16px 18px; + animation: rise .5s both; +} +.panel + .panel { margin-top: 18px; } +.p-title { margin: 0 0 14px; font-size: 9.5px; letter-spacing: 1.6px; text-transform: uppercase; color: var(--faint); font-weight: 600; } +.p-body { min-width: 0; } + +.kv { display: flex; justify-content: space-between; gap: 16px; padding: 6px 0; border-bottom: 1px dashed var(--line); } +.kv:last-child { border-bottom: none; } +.kv .k { color: var(--faint); } +.kv .v { color: var(--text); text-align: right; font-variant-numeric: tabular-nums; } + +/* ---- bars / gauges ---- */ +.bar { height: 6px; border-radius: 4px; background: var(--ink-4); overflow: hidden; } +.bar .fill { display: block; height: 100%; border-radius: 4px; transition: width .6s cubic-bezier(.4,0,.2,1); } +.fill.cool { background: linear-gradient(90deg, var(--cyan), #7fe3ec); } +.fill.warm { background: linear-gradient(90deg, var(--gold), var(--gold-2)); } +.fill.hot { background: linear-gradient(90deg, var(--warm), var(--bad)); } + +.mrow { margin-bottom: 13px; } +.mrow:last-child { margin-bottom: 0; } +.m-top { display: flex; justify-content: space-between; font-size: 11px; margin-bottom: 5px; } +.m-label { color: var(--dim); } +.m-val { color: var(--text); font-variant-numeric: tabular-nums; } + +/* ---- metrics: spark cards + scheduler ---- */ +.card { + background: var(--ink); border: 1px solid var(--line); border-radius: var(--r); + padding: 14px 16px; animation: rise .5s both; +} +.c-label { font-size: 9px; letter-spacing: 1.2px; text-transform: uppercase; color: var(--faint); } +.c-val { font-size: 21px; font-weight: 500; font-variant-numeric: tabular-nums; margin: 3px 0 8px; color: var(--gold-2); } +.spark-svg { width: 100%; height: 44px; display: block; } +.spark-svg polyline { fill: none; stroke: var(--cyan); stroke-width: 1.6; vector-effect: non-scaling-stroke; filter: drop-shadow(0 0 4px var(--cyan-soft)); } + +.sched { display: grid; grid-template-columns: 34px 1fr 44px; align-items: center; gap: 10px; padding: 4px 0; } +.s-id { font-size: 10.5px; color: var(--faint); } +.s-pct { font-size: 10.5px; text-align: right; color: var(--dim); font-variant-numeric: tabular-nums; } + +/* ---- toolbar + chips ---- */ +.toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; } +.t-label { font-size: 9px; letter-spacing: 1.2px; text-transform: uppercase; color: var(--faint); margin-right: 4px; } +.chip { + display: inline-block; font-family: var(--mono); font-size: 11px; letter-spacing: 0.4px; + color: var(--dim); background: var(--ink-3); border: 1px solid var(--line-2); + border-radius: 20px; padding: 5px 13px; cursor: pointer; transition: all .15s ease; +} +.chip:hover { color: var(--text); border-color: var(--faint); background: var(--ink-4); } +.chip.active { color: #1a1407; background: var(--gold); border-color: var(--gold); font-weight: 600; } +.chip.danger:hover { color: var(--bad); border-color: var(--bad); background: var(--bad-soft); } + +/* ---- tables ---- */ +.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r); } +.table { width: 100%; border-collapse: collapse; font-size: 12px; } +.table th { + text-align: left; padding: 10px 14px; font-size: 9px; letter-spacing: 1.2px; text-transform: uppercase; + color: var(--faint); font-weight: 600; background: var(--ink-2); border-bottom: 1px solid var(--line-2); position: sticky; top: 0; +} +.table td { padding: 8px 14px; border-bottom: 1px solid var(--line); color: var(--dim); white-space: nowrap; } +.table tbody tr:hover td { background: rgba(95, 212, 224, 0.04); color: var(--text); } +.table tbody tr:last-child td { border-bottom: none; } +.table .strong { color: var(--text); font-weight: 500; } +.table .mono { color: var(--faint); } +.table .num { text-align: right; font-variant-numeric: tabular-nums; color: var(--text); } +.empty-cell { text-align: center; color: var(--faint); font-style: italic; padding: 22px; } + +/* ---- supervision / spawn tree ---- */ +.tree { font-size: 12px; line-height: 1.4; } +.node { + display: flex; align-items: center; gap: 9px; padding: 4px 0; + padding-left: calc(var(--depth, 0) * 22px); +} +.node .n-kind { width: 15px; text-align: center; font-size: 11px; } +.n-kind.sup { color: var(--gold); } +.n-kind.worker { color: var(--cyan); } +.n-name { color: var(--text); } +.n-pid { color: var(--faint); font-size: 11px; } +.n-mem { margin-left: auto; color: var(--faint); font-size: 11px; font-variant-numeric: tabular-nums; } + +/* ---- request feed ---- */ +.feed { display: flex; flex-direction: column; gap: 4px; } +.req { + display: grid; grid-template-columns: 58px 1fr 52px 84px 96px 60px minmax(0, 1.4fr); align-items: center; gap: 12px; + padding: 9px 14px; border: 1px solid var(--line); border-radius: var(--r-sm); + background: var(--ink); transition: border-color .15s ease, background .15s ease; + animation: rowin .42s cubic-bezier(.4,0,.2,1) both; +} +.req:hover { border-color: var(--line-2); background: var(--ink-2); } +.method { font-size: 9.5px; font-weight: 600; letter-spacing: 0.6px; text-align: center; padding: 2px 0; border-radius: 4px; } +.method.get { color: var(--cyan); background: var(--cyan-soft); } +.method.post { color: var(--gold-2); background: var(--gold-soft); } +.method.put { color: var(--warm); background: rgba(232, 181, 74, 0.13); } +.method.delete { color: var(--bad); background: var(--bad-soft); } +.method.other { color: var(--faint); background: var(--ink-3); } +.r-path { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.status { font-size: 11px; text-align: center; font-variant-numeric: tabular-nums; font-weight: 500; } +.status.s2 { color: var(--ok); } .status.s3 { color: var(--cyan); } +.status.s4 { color: var(--warm); } .status.s5 { color: var(--bad); } +.r-dur, .r-reds { font-size: 11px; color: var(--dim); text-align: right; font-variant-numeric: tabular-nums; } +.r-spawn { font-size: 11px; color: var(--gold); text-align: center; } +.r-handler { font-size: 10.5px; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.req-head { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; } +.rh-path { font-size: 16px; color: var(--text); } + +.hint { max-width: 560px; margin: 40px auto; text-align: center; } +.hint h2 { font-size: 15px; font-weight: 600; letter-spacing: 0.5px; color: var(--gold-2); } +.hint p { color: var(--dim); } +.code { + text-align: left; font-family: var(--mono); font-size: 11.5px; color: var(--cyan); + background: var(--ink-2); border: 1px solid var(--line-2); border-radius: var(--r); + padding: 16px 18px; margin-top: 18px; overflow-x: auto; +} + +/* ---- status pips ---- */ +.pip { width: 8px; height: 8px; border-radius: 50%; flex: none; } +.pip.ok { background: var(--ok); box-shadow: 0 0 6px rgba(108, 219, 155, 0.6); } +.pip.warm { background: var(--warm); } +.pip.bad { background: var(--bad); box-shadow: 0 0 6px rgba(255, 111, 111, 0.6); animation: blink 1.3s steps(1) infinite; } +.pip.idle { background: var(--faint); } + +.empty { color: var(--faint); font-style: italic; padding: 18px 4px; } + +/* ---- motion ---- */ +@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(95, 212, 224, 0.5); } 70% { box-shadow: 0 0 0 7px rgba(95, 212, 224, 0); } 100% { box-shadow: 0 0 0 0 rgba(95, 212, 224, 0); } } +@keyframes blink { 50% { opacity: 0.3; } } +@keyframes twinkle { 0%, 100% { opacity: 1; } 45% { opacity: 0.55; } } +@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } } +@keyframes rowin { from { opacity: 0; transform: translateY(-7px); } to { opacity: 1; transform: none; } } + +@media (max-width: 720px) { + .rail { width: 56px; } .rail .item .label, .nav-sep { display: none; } + .req { grid-template-columns: 52px 1fr 48px; } .req .r-dur, .req .r-reds, .req .r-spawn, .req .r-handler { display: none; } +} diff --git a/priv/static/assets/css/liveboard.css b/priv/static/assets/css/liveboard.css deleted file mode 100644 index 3b4f30e..0000000 --- a/priv/static/assets/css/liveboard.css +++ /dev/null @@ -1,63 +0,0 @@ -* { margin: 0; padding: 0; box-sizing: border-box; } -body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0f172a; color: #e2e8f0; } -.nav { background: #1e293b; border-bottom: 1px solid #334155; padding: 0 1.5rem; display: flex; align-items: center; gap: 2rem; height: 3.5rem; } -.nav-brand { font-size: 1.1rem; font-weight: 700; color: #38bdf8; letter-spacing: -0.02em; } -.nav-links { display: flex; gap: 0.25rem; } -.nav-links a { padding: 0.5rem 1rem; color: #94a3b8; text-decoration: none; border-radius: 0.375rem; font-size: 0.875rem; font-weight: 500; transition: all 0.15s; } -.nav-links a:hover { color: #e2e8f0; background: #334155; } -.nav-links a.active { color: #38bdf8; background: #0f172a; } -.main { padding: 1.5rem; max-width: 90rem; margin: 0 auto; } -.card { background: #1e293b; border: 1px solid #334155; border-radius: 0.5rem; padding: 1.25rem; margin-bottom: 1rem; } -.card-title { font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #64748b; margin-bottom: 0.75rem; } -.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: 1rem; margin-bottom: 1.5rem; } -.stat { background: #1e293b; border: 1px solid #334155; border-radius: 0.5rem; padding: 1rem; } -.stat-label { font-size: 0.75rem; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; } -.stat-value { font-size: 1.5rem; font-weight: 700; color: #f1f5f9; margin-top: 0.25rem; font-variant-numeric: tabular-nums; } -.stat-sub { font-size: 0.75rem; color: #475569; margin-top: 0.125rem; } -table { width: 100%; border-collapse: collapse; font-size: 0.875rem; } -th { text-align: left; padding: 0.625rem 0.75rem; color: #64748b; font-weight: 600; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; border-bottom: 1px solid #334155; } -td { padding: 0.5rem 0.75rem; border-bottom: 1px solid #1e293b; font-variant-numeric: tabular-nums; } -tr:hover td { background: #1e293b; } -.mono { font-family: "SF Mono", "Cascadia Code", "Fira Code", monospace; font-size: 0.8125rem; } -.text-right { text-align: right; } -.text-blue { color: #38bdf8; } -.text-green { color: #4ade80; } -.text-amber { color: #fbbf24; } -.text-dim { color: #475569; } -.bar { height: 0.375rem; background: #334155; border-radius: 9999px; overflow: hidden; } -.bar-fill { height: 100%; border-radius: 9999px; transition: width 0.3s; } -.bar-fill-blue { background: #38bdf8; } -.bar-fill-green { background: #4ade80; } -.bar-fill-amber { background: #fbbf24; } -.bar-fill-red { background: #f87171; } -.sort-btn { background: none; border: none; color: #64748b; cursor: pointer; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; padding: 0.625rem 0.75rem; width: 100%; text-align: left; } -.sort-btn:hover { color: #e2e8f0; } -.sort-btn.active { color: #38bdf8; } -.badge { display: inline-block; padding: 0.125rem 0.5rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 500; } -.badge-green { background: #166534; color: #4ade80; } -.badge-blue { background: #1e3a5f; color: #38bdf8; } -.badge-amber { background: #78350f; color: #fbbf24; } -.text-red { color: #f87171; } -.nav-sep { width: 1px; height: 1.25rem; background: #334155; margin: 0 0.25rem; } -.refresh-info { font-size: 0.75rem; color: #475569; text-align: right; margin-bottom: 0.5rem; } - -/* Supervision Tree */ -.app-selector { display: flex; flex-wrap: wrap; gap: 0.375rem; margin-bottom: 1rem; } -.app-btn { background: #1e293b; border: 1px solid #334155; color: #94a3b8; padding: 0.375rem 0.75rem; border-radius: 0.375rem; font-size: 0.8125rem; cursor: pointer; transition: all 0.15s; } -.app-btn:hover { color: #e2e8f0; border-color: #475569; } -.app-btn.active { color: #38bdf8; border-color: #38bdf8; background: #0f172a; } -.tree-flat { display: flex; flex-direction: column; } -.tree-row { display: flex; align-items: center; gap: 0.5rem; padding: 0.375rem 0.5rem; border-radius: 0.375rem; border-bottom: 1px solid #1e293b; } -.tree-row:hover { background: #334155; } -.tree-meta { display: flex; gap: 0.75rem; margin-left: auto; font-size: 0.8125rem; align-items: center; } - -/* Metrics */ -.metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem; margin-bottom: 1.5rem; } -.metric-card { background: #1e293b; border: 1px solid #334155; border-radius: 0.5rem; padding: 1rem; } -.metric-current { font-size: 1.5rem; font-weight: 700; color: #f1f5f9; margin: 0.25rem 0 0.5rem; font-variant-numeric: tabular-nums; } -.sparkline { width: 100%; height: 40px; display: block; } -.sparkline polyline { fill: none; stroke: #38bdf8; stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; } -.scheduler-bars { display: flex; flex-direction: column; gap: 0.375rem; padding: 0.5rem; } -.sched-row { display: flex; align-items: center; gap: 0.75rem; } -.sched-label { width: 2rem; text-align: right; font-size: 0.8125rem; color: #64748b; } -.sched-pct { width: 3rem; text-align: right; font-size: 0.8125rem; color: #94a3b8; } diff --git a/priv/static/assets/fonts/NOTICE b/priv/static/assets/fonts/NOTICE new file mode 100644 index 0000000..3c354ec --- /dev/null +++ b/priv/static/assets/fonts/NOTICE @@ -0,0 +1,13 @@ +Self-hosted web fonts for gakudan_liveboard. + +These WOFF2 files are bundled so the dashboard makes no off-origin requests +(GDPR by design). They are fetched by scripts/fetch-fonts.sh. Both families are +licensed under the SIL Open Font License 1.1 (OFL), which permits bundling and +redistribution: + +- IBM Plex Mono - (c) IBM Corp. - https://github.com/IBM/plex +- Instrument Serif- (c) Rosetta Type / Instrument - https://github.com/Instrument/instrument-serif + +SIL OFL 1.1: https://openfontlicense.org/ + +Latin subset only; regenerate with `make fonts` (scripts/fetch-fonts.sh). diff --git a/priv/static/assets/fonts/ibm-plex-mono-latin-400-italic.woff2 b/priv/static/assets/fonts/ibm-plex-mono-latin-400-italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..855c41c031b6853fd2652a826f171f7498ae5d04 GIT binary patch literal 16448 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 literal 0 HcmV?d00001 diff --git a/priv/static/assets/fonts/ibm-plex-mono-latin-400-normal.woff2 b/priv/static/assets/fonts/ibm-plex-mono-latin-400-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0804aaff92260b5359330f63e05b9ac25ccabeee GIT binary patch literal 14708 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 literal 0 HcmV?d00001 diff --git a/priv/static/assets/js/arizona.min.js b/priv/static/assets/js/arizona.min.js deleted file mode 100644 index 54f676d..0000000 --- a/priv/static/assets/js/arizona.min.js +++ /dev/null @@ -1,534 +0,0 @@ -const Z = '(function(){"use strict";class n{constructor(){this.structure=new Map}initialize(t){this.structure=new Map(Object.entries(JSON.parse(JSON.stringify(t))))}mergeStructures(t){for(const[e,s]of Object.entries(t))this.structure.set(e,s)}applyDiff(t,e){if(e?.type==="stateful"){this.structure.set(e.id,e);return}if(!this.structure.has(t)){const r=String(t).replace(/\\r|\\n/g,"");console.warn(`[Arizona] StatefulId \'${r}\' not found in structure`)}const s=this.structure.get(t);for(const[r,i]of e)this.applyDiffValue(s.dynamic,r-1,i)}applyDiffValue(t,e,s){const r=t[e];if(r?.type&&Array.isArray(s))switch(r.type){case"stateful":{const i=this.structure.get(r.id);if(!i){const a=String(r.id).replace(/\\r|\\n/g,"");console.warn(`[Arizona] Component \'${a}\' not found in structure`);return}s.forEach(([a,h])=>{this.applyDiffValue(i.dynamic,a-1,h)});return}case"stateless":s.forEach(([i,a])=>{this.applyDiffValue(r.dynamic,i-1,a)});return;case"list":r.dynamic=s;return}t[e]=s}generateStatefulHTML(t){const e=this.structure.get(t);if(!e){const r=String(t).replace(/\\r|\\n/g,"");throw console.warn(`[Arizona] StatefulId \'${r}\' not found in structure`),new Error(`Component ${r} not found`)}return this.zipStaticDynamic(e.static,e.dynamic)}generateStatelessHTML(t){return this.zipStaticDynamic(t.static,t.dynamic)}generateListHTML(t){const{static:e,dynamic:s}=t;return s.reduce((r,i)=>r+this.zipStaticDynamic(e,i),"")}zipStaticDynamic(t,e){const s=[],r=Math.max(t.length,e.length);for(let i=0;ithis.flattenIoData(e)).join(""):t&&typeof t=="object"?t.type==="stateful"?this.generateStatefulHTML(t.id):t.type==="stateless"?this.generateStatelessHTML(t):t.type==="list"?this.generateListHTML(t):String(t):String(t||"")}getStructure(){return JSON.parse(JSON.stringify(Object.fromEntries(this.structure)))}isInitialized(){return this.structure.size>0}getComponentIds(){return Array.from(this.structure.keys())}clear(){this.structure=new Map}createPatch(t){const e=this.generateStatefulHTML(t);return{type:"html_patch",statefulId:t,html:e}}}class c{constructor(){this.socket=null,this.connected=!1,this.messageQueue=[],this.hierarchical=new n,self.onmessage=t=>{if(!t.data||typeof t.data!="object"||!t.data.type)return;const{type:e,data:s}=t.data;switch(e){case"connect":this.connect(s.url,s.expectedHost);break;case"send":this.sendMessage(s);break;case"disconnect":this.disconnect();break}}}connect(t,e){if(!this.connected){if(!this.isValidWebSocketUrl(t,e)){this.postMessage({type:"error",data:{error:"Invalid WebSocket URL"}});return}this.socket=new WebSocket(t),this.socket.onopen=()=>{this.connected=!0,this.postMessage({type:"status",data:{status:"connected"}}),this.flushMessageQueue()},this.socket.onmessage=s=>{const r=JSON.parse(s.data);this.handleWebSocketMessage(r)},this.socket.onclose=()=>{this.connected=!1,this.postMessage({type:"status",data:{status:"disconnected"}})},this.socket.onerror=s=>{this.postMessage({type:"error",data:{error:s.toString()}})}}}isValidWebSocketUrl(t,e){try{const s=new URL(t);return!(s.protocol!=="ws:"&&s.protocol!=="wss:"||e&&s.host!==e||s.pathname.includes("../")||s.pathname.includes("..\\\\"))}catch{return!1}}sendMessage(t){const e=JSON.stringify(t);this.connected&&this.socket.readyState===WebSocket.OPEN?this.socket.send(e):this.messageQueue.push(e)}flushMessageQueue(){for(;this.messageQueue.length>0;){const t=this.messageQueue.shift();if(this.socket.readyState===WebSocket.OPEN)this.socket.send(t);else{this.messageQueue.unshift(t);break}}}disconnect(){this.socket&&(this.socket.close(),this.socket=null),this.connected=!1,this.hierarchical.clear()}handleWebSocketMessage(t){try{switch(t.type){case"initial_render":this.handleInitialRender(t);break;case"diff":this.handleDiff(t);break;case"reload":this.handleReload(t);break;case"dispatch":this.handleDispatch(t);break;case"reply":this.handleReply(t);break;case"redirect":this.handleRedirect(t);break;default:this.handleUnknownMessage(t)}}catch(e){this.postMessage({type:"error",data:{error:`Message handling failed: ${e.message}`}})}}handleInitialRender(t){this.hierarchical.initialize(t.structure)}handleDiff(t){if(!this.hierarchical.isInitialized())throw new Error("Hierarchical structure not initialized");t.structure&&Object.keys(t.structure).length>0&&this.hierarchical.mergeStructures(t.structure),this.hierarchical.applyDiff(t.stateful_id,t.changes);const e=this.hierarchical.createPatch(t.stateful_id);this.postMessage({type:"html_patch",data:{patch:e}})}handleReload(t){this.postMessage({type:"reload",data:t})}handleDispatch(t){this.postMessage({type:"dispatch",data:t})}handleReply(t){this.postMessage({type:"reply",data:t})}handleRedirect(t){this.postMessage({type:"redirect",data:{url:t.url,target:t.target}})}handleUnknownMessage(t){this.postMessage(t)}postMessage(t){self.postMessage(t)}}new c})();\n//# sourceMappingURL=arizona-worker.min.js.map\n', G = typeof self < "u" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", Z], { type: "text/javascript;charset=utf-8" }); -function le(n) { - let e; - try { - if (e = G && (self.URL || self.webkitURL).createObjectURL(G), !e) throw ""; - const t = new Worker(e, { - name: n?.name - }); - return t.addEventListener("error", () => { - (self.URL || self.webkitURL).revokeObjectURL(e); - }), t; - } catch { - return new Worker( - "data:text/javascript;charset=utf-8," + encodeURIComponent(Z), - { - name: n?.name - } - ); - } -} -var J = 11; -function oe(n, e) { - var t = e.attributes, r, s, l, f, p; - if (!(e.nodeType === J || n.nodeType === J)) { - for (var d = t.length - 1; d >= 0; d--) - r = t[d], s = r.name, l = r.namespaceURI, f = r.value, l ? (s = r.localName || s, p = n.getAttributeNS(l, s), p !== f && (r.prefix === "xmlns" && (s = r.name), n.setAttributeNS(l, s, f))) : (p = n.getAttribute(s), p !== f && n.setAttribute(s, f)); - for (var b = n.attributes, T = b.length - 1; T >= 0; T--) - r = b[T], s = r.name, l = r.namespaceURI, l ? (s = r.localName || s, e.hasAttributeNS(l, s) || n.removeAttributeNS(l, s)) : e.hasAttribute(s) || n.removeAttribute(s); - } -} -var E, he = "http://www.w3.org/1999/xhtml", g = typeof document > "u" ? void 0 : document, de = !!g && "content" in g.createElement("template"), ue = !!g && g.createRange && "createContextualFragment" in g.createRange(); -function fe(n) { - var e = g.createElement("template"); - return e.innerHTML = n, e.content.childNodes[0]; -} -function ge(n) { - E || (E = g.createRange(), E.selectNode(g.body)); - var e = E.createContextualFragment(n); - return e.childNodes[0]; -} -function pe(n) { - var e = g.createElement("body"); - return e.innerHTML = n, e.childNodes[0]; -} -function ve(n) { - return n = n.trim(), de ? fe(n) : ue ? ge(n) : pe(n); -} -function _(n, e) { - var t = n.nodeName, r = e.nodeName, s, l; - return t === r ? !0 : (s = t.charCodeAt(0), l = r.charCodeAt(0), s <= 90 && l >= 97 ? t === r.toUpperCase() : l <= 90 && s >= 97 ? r === t.toUpperCase() : !1); -} -function ye(n, e) { - return !e || e === he ? g.createElement(n) : g.createElementNS(e, n); -} -function me(n, e) { - for (var t = n.firstChild; t; ) { - var r = t.nextSibling; - e.appendChild(t), t = r; - } - return e; -} -function j(n, e, t) { - n[t] !== e[t] && (n[t] = e[t], n[t] ? n.setAttribute(t, "") : n.removeAttribute(t)); -} -var q = { - OPTION: function(n, e) { - var t = n.parentNode; - if (t) { - var r = t.nodeName.toUpperCase(); - r === "OPTGROUP" && (t = t.parentNode, r = t && t.nodeName.toUpperCase()), r === "SELECT" && !t.hasAttribute("multiple") && (n.hasAttribute("selected") && !e.selected && (n.setAttribute("selected", "selected"), n.removeAttribute("selected")), t.selectedIndex = -1); - } - j(n, e, "selected"); - }, - /** - * The "value" attribute is special for the element since it sets - * the initial value. Changing the "value" attribute without changing the - * "value" property will have no effect since it is only used to the set the - * initial value. Similar for the "checked" attribute, and "disabled". - */ - INPUT: function(n, e) { - j(n, e, "checked"), j(n, e, "disabled"), n.value !== e.value && (n.value = e.value), e.hasAttribute("value") || n.removeAttribute("value"); - }, - TEXTAREA: function(n, e) { - var t = e.value; - n.value !== t && (n.value = t); - var r = n.firstChild; - if (r) { - var s = r.nodeValue; - if (s == t || !t && s == n.placeholder) - return; - r.nodeValue = t; - } - }, - SELECT: function(n, e) { - if (!e.hasAttribute("multiple")) { - for (var t = -1, r = 0, s = n.firstChild, l, f; s; ) - if (f = s.nodeName && s.nodeName.toUpperCase(), f === "OPTGROUP") - l = s, s = l.firstChild, s || (s = l.nextSibling, l = null); - else { - if (f === "OPTION") { - if (s.hasAttribute("selected")) { - t = r; - break; - } - r++; - } - s = s.nextSibling, !s && l && (s = l.nextSibling, l = null); - } - n.selectedIndex = t; - } - } -}, A = 1, K = 11, X = 3, Y = 8; -function w() { -} -function we(n) { - if (n) - return n.getAttribute && n.getAttribute("id") || n.id; -} -function be(n) { - return function(t, r, s) { - if (s || (s = {}), typeof r == "string") - if (t.nodeName === "#document" || t.nodeName === "HTML") { - var l = r; - r = g.createElement("html"), r.innerHTML = l; - } else if (t.nodeName === "BODY") { - var f = r; - r = g.createElement("html"), r.innerHTML = f; - var p = r.querySelector("body"); - p && (r = p); - } else - r = ve(r); - else r.nodeType === K && (r = r.firstElementChild); - var d = s.getNodeKey || we, b = s.onBeforeNodeAdded || w, T = s.onNodeAdded || w, ee = s.onBeforeElUpdated || w, te = s.onElUpdated || w, re = s.onBeforeNodeDiscarded || w, L = s.onNodeDiscarded || w, se = s.onBeforeElChildrenUpdated || w, ne = s.skipFromChildren || w, V = s.addChild || function(i, a) { - return i.appendChild(a); - }, x = s.childrenOnly === !0, k = /* @__PURE__ */ Object.create(null), R = []; - function U(i) { - R.push(i); - } - function B(i, a) { - if (i.nodeType === A) - for (var h = i.firstChild; h; ) { - var c = void 0; - a && (c = d(h)) ? U(c) : (L(h), h.firstChild && B(h, a)), h = h.nextSibling; - } - } - function D(i, a, h) { - re(i) !== !1 && (a && a.removeChild(i), L(i), B(i, h)); - } - function I(i) { - if (i.nodeType === A || i.nodeType === K) - for (var a = i.firstChild; a; ) { - var h = d(a); - h && (k[h] = a), I(a), a = a.nextSibling; - } - } - I(t); - function z(i) { - T(i); - for (var a = i.firstChild; a; ) { - var h = a.nextSibling, c = d(a); - if (c) { - var o = k[c]; - o && _(a, o) ? (a.parentNode.replaceChild(o, a), O(o, a)) : z(a); - } else - z(a); - a = h; - } - } - function ie(i, a, h) { - for (; a; ) { - var c = a.nextSibling; - (h = d(a)) ? U(h) : D( - a, - i, - !0 - /* skip keyed nodes */ - ), a = c; - } - } - function O(i, a, h) { - var c = d(a); - if (c && delete k[c], !h) { - var o = ee(i, a); - if (o === !1 || (o instanceof HTMLElement && (i = o, I(i)), n(i, a), te(i), se(i, a) === !1)) - return; - } - i.nodeName !== "TEXTAREA" ? ae(i, a) : q.TEXTAREA(i, a); - } - function ae(i, a) { - var h = ne(i, a), c = a.firstChild, o = i.firstChild, S, v, M, N, y; - e: for (; c; ) { - for (N = c.nextSibling, S = d(c); !h && o; ) { - if (M = o.nextSibling, c.isSameNode && c.isSameNode(o)) { - c = N, o = M; - continue e; - } - v = d(o); - var H = o.nodeType, m = void 0; - if (H === c.nodeType && (H === A ? (S ? S !== v && ((y = k[S]) ? M === y ? m = !1 : (i.insertBefore(y, o), v ? U(v) : D( - o, - i, - !0 - /* skip keyed nodes */ - ), o = y, v = d(o)) : m = !1) : v && (m = !1), m = m !== !1 && _(o, c), m && O(o, c)) : (H === X || H == Y) && (m = !0, o.nodeValue !== c.nodeValue && (o.nodeValue = c.nodeValue))), m) { - c = N, o = M; - continue e; - } - v ? U(v) : D( - o, - i, - !0 - /* skip keyed nodes */ - ), o = M; - } - if (S && (y = k[S]) && _(y, c)) - h || V(i, y), O(y, c); - else { - var W = b(c); - W !== !1 && (W && (c = W), c.actualize && (c = c.actualize(i.ownerDocument || g)), V(i, c), z(c)); - } - c = N, o = M; - } - ie(i, o, v); - var Q = q[i.nodeName]; - Q && Q(i, a); - } - var u = t, C = u.nodeType, F = r.nodeType; - if (!x) { - if (C === A) - F === A ? _(t, r) || (L(t), u = me(t, ye(r.nodeName, r.namespaceURI))) : u = r; - else if (C === X || C === Y) { - if (F === C) - return u.nodeValue !== r.nodeValue && (u.nodeValue = r.nodeValue), u; - u = r; - } - } - if (u === r) - L(t); - else { - if (r.isSameNode && r.isSameNode(u)) - return; - if (O(u, r, x), R) - for (var P = 0, ce = R.length; P < ce; P++) { - var $ = k[R[P]]; - $ && D($, $.parentNode, !1); - } - } - return !x && u !== t && t.parentNode && (u.actualize && (u = u.actualize(t.ownerDocument || g)), t.parentNode.replaceChild(u, t)), u; - }; -} -var ke = be(oe); -class Se { - /** - * Creates a new Arizona client instance - * @param {ArizonaOptions} [opts={}] - Client configuration options - */ - constructor(e = {}) { - this.worker = null, this.connected = !1, this.eventListeners = /* @__PURE__ */ new Map(), this.logger = e.logger || null, this.nextRefId = 0, this.pendingCalls = /* @__PURE__ */ new Map(); - } - /** - * Initialize worker if not already created - * @private - * @returns {void} - */ - initializeWorker() { - this.worker || (this.worker = new le(), this.worker.onmessage = (e) => { - this.handleWorkerMessage(e.data); - }); - } - /** - * Connect to the Arizona WebSocket server - * @param {string} websocketEndpoint - WebSocket endpoint path - * @returns {void} - */ - connect(e) { - if (this.connected) return; - this.initializeWorker(); - const t = window.location.protocol === "https:" ? "wss:" : "ws:", r = window.location.host, s = window.location.pathname, l = window.location.search, f = encodeURIComponent(s), p = l ? encodeURIComponent(l.substring(1)) : "", d = `${t}//${r}${e}?path=${f}&qs=${p}`; - this.worker.postMessage({ - type: "connect", - data: { url: d, expectedHost: r } - }); - } - /** - * Push an event to the Arizona server - * @param {string} event - Event name - * @param {EventParams} [params={}] - Event parameters - * @returns {void} - */ - pushEvent(e, t = {}) { - this.connected && this.worker.postMessage({ - type: "send", - data: { - type: "event", - event: e, - params: t - } - }); - } - /** - * Push an event to a specific stateful component - * @param {string} statefulId - Target stateful component ID - * @param {string} event - Event name - * @param {EventParams} [params={}] - Event parameters - * @returns {void} - */ - pushEventTo(e, t, r = {}) { - this.connected && this.worker.postMessage({ - type: "send", - data: { - type: "event", - stateful_id: e, - event: t, - params: r - } - }); - } - /** - * Call an event on the Arizona server and wait for reply - * @param {string} event - Event name - * @param {EventParams} [params={}] - Event parameters - * @param {Object} [options={}] - Call options - * @param {number} [options.timeout=10000] - Timeout in milliseconds - * @returns {Promise<*>} Promise that resolves with reply data - */ - callEvent(e, t = {}, r = {}) { - return this._callEvent(void 0, e, t, r); - } - /** - * Call an event on a specific stateful component and wait for reply - * @param {string} statefulId - Target stateful component ID - * @param {string} event - Event name - * @param {EventParams} [params={}] - Event parameters - * @param {Object} [options={}] - Call options - * @param {number} [options.timeout=10000] - Timeout in milliseconds - * @returns {Promise<*>} Promise that resolves with reply data - */ - callEventFrom(e, t, r = {}, s = {}) { - return this._callEvent(e, t, r, s); - } - /** - * Internal helper to call an event and wait for reply - * @private - * @param {string|undefined} statefulId - Target stateful component ID (undefined for view) - * @param {string} event - Event name - * @param {EventParams} params - Event parameters - * @param {Object} options - Call options - * @returns {Promise<*>} Promise that resolves with reply data - */ - _callEvent(e, t, r, s) { - if (!this.connected) return Promise.reject(new Error("Not connected")); - const l = `${++this.nextRefId}`; - return new Promise((f, p) => { - const d = setTimeout(() => { - this.pendingCalls.delete(l), p(new Error(`Call timeout: ${t}`)); - }, s.timeout || 1e4); - this.pendingCalls.set(l, { resolve: f, reject: p, timeout: d }); - const b = { - type: "event", - ref_id: l, - event: t, - params: r - }; - e !== void 0 && (b.stateful_id = e), this.worker.postMessage({ - type: "send", - data: b - }); - }); - } - /** - * Disconnect from the Arizona WebSocket server - * @returns {void} - */ - disconnect() { - this.worker && (this.worker.terminate(), this.worker = null), this.connected = !1, this.pendingCalls.forEach((e) => { - clearTimeout(e.timeout), e.reject(new Error("Disconnected")); - }), this.pendingCalls.clear(); - } - /** - * Handle messages from the worker thread - * @private - * @param {Object} message - Worker message - * @returns {void} - */ - handleWorkerMessage(e) { - const { type: t, data: r } = e; - try { - switch (t) { - case "status": - this.handleStatus(r); - break; - case "initial_render": - break; - case "html_patch": - this.handleHtmlPatch(r); - break; - case "error": - this.handleWorkerError(r); - break; - case "reload": - this.handleReload(r); - break; - case "dispatch": - this.handleDispatch(r); - break; - case "reply": - this.handleReply(r); - break; - case "redirect": - this.handleRedirect(r); - break; - default: - this.handleUnknownMessage(e); - } - } catch (s) { - this.logger?.error("Error handling worker message:", s); - } - } - handleStatus(e) { - e.status === "connected" ? (this.connected = !0, this.logger?.info("Connected to WebSocket"), this.emit("connected", e)) : e.status === "disconnected" && (this.connected = !1, this.logger?.info("Disconnected from WebSocket"), this.emit("disconnected", e)); - } - handleHtmlPatch(e) { - this.logger?.debug("Applying HTML patch"), this.applyHtmlPatch(e.patch); - } - applyHtmlPatch(e) { - const t = document.getElementById(e.statefulId); - if (!t) { - console.warn("[Arizona] Target element not found:", e.statefulId), this.logger?.warning(`Target element not found: ${e.statefulId}`); - return; - } - try { - ke(t, e.html, { - onBeforeElUpdated(r, s) { - return s.dataset?.arizonaUpdate === "false" ? !1 : !r.isEqualNode(s); - } - }), this.logger?.debug("Patch applied successfully"); - } catch (r) { - this.logger?.error("Error applying HTML patch:", r); - } - } - handleWorkerError(e) { - this.logger?.error("Worker Error:", e.error), this.emit("error", e); - } - handleReload(e) { - e.file_type === "css" ? (this.logger?.info("CSS file changed. Refreshing stylesheets without page reload..."), document.querySelectorAll('link[rel="stylesheet"]').forEach((t) => { - const r = t.href.split("?")[0]; - t.href = `${r}?t=${Date.now()}`; - })) : (this.logger?.info(`${e.file_type || "File"} changed. Reloading page...`), window.location.reload()); - } - handleDispatch(e) { - this.logger?.debug("Dispatching event:", e.event), this.emit(e.event, e.data); - } - handleReply(e) { - const { ref_id: t, data: r } = e, s = this.pendingCalls.get(t); - s ? (clearTimeout(s.timeout), s.resolve(r), this.pendingCalls.delete(t), this.logger?.debug(`Reply received for ref: ${t}`)) : this.logger?.warning(`Received reply for unknown ref: ${t}`); - } - handleRedirect(e) { - this.logger?.info("Redirecting to:", e.url), window.open(e.url, e.options?.target, e.options?.window_features); - } - handleUnknownMessage(e) { - this.logger?.warning("Unknown worker message:", e); - } - /** - * Check if client is connected to server - * @returns {boolean} True if connected - */ - isConnected() { - return this.connected; - } - /** - * Subscribe to an Arizona event - * @param {string} event - Event name (e.g., 'connected', 'disconnected') - * @param {Function} callback - Callback function to invoke when event occurs - * @returns {Function} Unsubscribe function - */ - on(e, t) { - return typeof t != "function" ? (this.logger?.error(`on: callback must be a function, got ${typeof t}`), () => { - }) : (this.eventListeners.has(e) || this.eventListeners.set(e, /* @__PURE__ */ new Set()), this.eventListeners.get(e).add(t), this.logger?.debug(`Subscribed to event: ${e}`), () => this.off(e, t)); - } - /** - * Subscribe to an Arizona event that will only fire once - * @param {string} event - Event name - * @param {Function} callback - Callback function to invoke when event occurs - * @returns {Function} Unsubscribe function - */ - once(e, t) { - if (typeof t != "function") - return this.logger?.error(`once: callback must be a function, got ${typeof t}`), () => { - }; - const r = (s) => { - t(s), this.off(e, r); - }; - return this.on(e, r); - } - /** - * Unsubscribe from an Arizona event - * @param {string} event - Event name - * @param {Function} callback - Callback function to remove - * @returns {void} - */ - off(e, t) { - const r = this.eventListeners.get(e); - r && (r.delete(t), this.logger?.debug(`Unsubscribed from event: ${e}`), r.size === 0 && this.eventListeners.delete(e)); - } - /** - * Remove all listeners for a specific event, or all events if no event specified - * @param {string} [event] - Optional event name. If not provided, removes all listeners for all events - * @returns {void} - */ - removeAllListeners(e) { - e ? (this.eventListeners.delete(e), this.logger?.debug(`Removed all listeners for event: ${e}`)) : (this.eventListeners.clear(), this.logger?.debug("Removed all event listeners")); - } - /** - * Emit an Arizona event to all subscribed listeners - * @private - * @param {string} event - Event name - * @param {*} data - Event data to pass to listeners - * @returns {void} - */ - emit(e, t) { - const r = this.eventListeners.get(e); - r && r.forEach((s) => { - try { - s(t); - } catch (l) { - this.logger?.error(`Error in event listener for '${e}':`, l); - } - }); - } -} -export { - Se as default -}; -//# sourceMappingURL=arizona.min.js.map diff --git a/priv/static/assets/js/datastar.js b/priv/static/assets/js/datastar.js new file mode 100644 index 0000000..3cceb55 --- /dev/null +++ b/priv/static/assets/js/datastar.js @@ -0,0 +1,9 @@ +// Datastar v1.0.0 +var yt=/🖕JS_DS🚀/.source,Ue=yt.slice(0,5),Je=yt.slice(4),B="datastar-fetch",te="datastar-prop-change",vt="datastar-ready",Ke="datastar-scope-children",ne="datastar-signal-patch";var x=Object.hasOwn??Object.prototype.hasOwnProperty.call;var K=e=>e!==null&&typeof e=="object"&&(Object.getPrototypeOf(e)===Object.prototype||Object.getPrototypeOf(e)===null),bt=e=>{for(let t in e)if(x(e,t))return!1;return!0},re=(e,t)=>{for(let n in e){let r=e[n];K(r)||Array.isArray(r)?re(r,t):e[n]=t(r)}},Le=e=>{let t={};for(let[n,r]of e){let s=n.split("."),i=s.pop(),o=s.reduce((a,c)=>a[c]??={},t);o[i]=r}return t};var xe=[],ze=[],He=0,Ne=0,Ze=0,Qe,j,Pe=0,N=()=>{He++},P=()=>{--He||(Tt(),z())},_=e=>{Qe=j,j=e},k=()=>{j=Qe,Qe=void 0},me=e=>cn.bind(0,{previousValue:e,t:e,e:1}),Ye=Symbol("computed"),_e=e=>{let t=ln.bind(0,{e:17,getter:e});return t[Ye]=1,t},R=e=>{let t={d:e,e:2};j&&et(t,j),_(t),N();try{t.d()}finally{P(),k()}return Mt.bind(0,t)},Tt=()=>{for(;Ne"getter"in e?At(e):Rt(e,e.t),At=e=>{_(e),Lt(e);try{let t=e.t;return t!==(e.t=e.getter(t))}finally{k(),xt(e)}},Rt=(e,t)=>(e.e=1,e.previousValue!==(e.previousValue=t)),Xe=e=>{let t=e.e;if(!(t&64)){e.e=t|64;let n=e.r;n?Xe(n.o):ze[Ze++]=e}},wt=(e,t)=>{if(t&16||t&32&&Nt(e.s,e)){_(e),Lt(e),N();try{e.d()}finally{P(),k(),xt(e)}return}t&32&&(e.e=t&-33);let n=e.s;for(;n;){let r=n.c,s=r.e;s&64&&wt(r,r.e=s&-65),n=n.i}},cn=(e,...t)=>{if(t.length){if(e.t!==(e.t=t[0])){e.e=17;let r=e.r;return r&&(un(r),He||Tt()),!0}return!1}let n=e.t;if(e.e&16&&Rt(e,n)){let r=e.r;r&&Fe(r)}return j&&et(e,j),n},ln=e=>{let t=e.e;if(t&16||t&32&&Nt(e.s,e)){if(At(e)){let n=e.r;n&&Fe(n)}}else t&32&&(e.e=t&-33);return j&&et(e,j),e.t},Mt=e=>{let t=e.s;for(;t;)t=Ce(t,e);let n=e.r;n&&Ce(n),e.e=0},et=(e,t)=>{let n=t.a;if(n&&n.c===e)return;let r=n?n.i:t.s;if(r&&r.c===e){r.p=Pe,t.a=r;return}let s=e.m;if(s&&s.p===Pe&&s.o===t)return;let i=t.a=e.m={p:Pe,c:e,o:t,l:n,i:r,u:s};r&&(r.l=i),n?n.i=i:t.s=i,s?s.n=i:e.r=i},Ce=(e,t=e.o)=>{let n=e.c,r=e.l,s=e.i,i=e.n,o=e.u;if(s?s.l=r:t.a=r,r?r.i=s:t.s=s,i?i.u=o:n.m=o,o)o.n=i;else if(!(n.r=i))if("getter"in n){let a=n.s;if(a){n.e=17;do a=Ce(a,n);while(a)}}else"previousValue"in n||Mt(n);return s},un=e=>{let t=e.n,n;e:for(;;){let r=e.o,s=r.e;if(s&60?s&12?s&4?!(s&48)&&fn(e,r)?(r.e=s|40,s&=1):s=0:r.e=s&-9|32:s=0:r.e=s|32,s&2&&Xe(r),s&1){let i=r.r;if(i){let o=(e=i).n;o&&(n={t,f:n},t=o);continue}}if(e=t){t=e.n;continue}for(;n;)if(e=n.t,n=n.f,e){t=e.n;continue e}break}},Lt=e=>{Pe++,e.a=void 0,e.e=e.e&-57|4},xt=e=>{let t=e.a,n=t?t.i:e.s;for(;n;)n=Ce(n,e);e.e&=-5},Nt=(e,t)=>{let n,r=0,s=!1;e:for(;;){let i=e.c,o=i.e;if(t.e&16)s=!0;else if((o&17)===17){if(Et(i)){let a=i.r;a.n&&Fe(a),s=!0}}else if((o&33)===33){(e.n||e.u)&&(n={t:e,f:n}),e=i.s,t=i,++r;continue}if(!s){let a=e.i;if(a){e=a;continue}}for(;r--;){let a=t.r,c=a.n;if(c?(e=n.t,n=n.f):e=a,s){if(Et(t)){c&&Fe(a),t=e.o;continue}s=!1}else t.e&=-33;if(t=e.o,e.i){e=e.i;continue e}}return s}},Fe=e=>{do{let t=e.o,n=t.e;(n&48)===32&&(t.e=n|16,n&2&&Xe(t))}while(e=e.n)},fn=(e,t)=>{let n=t.a;for(;n;){if(n===e)return!0;n=n.l}return!1},W=e=>{let t=se,n=e.split(".");for(let r of n){if(t==null||!x(t,r))return;t=t[r]}return t},Oe=(e,t="")=>{let n=Array.isArray(e);if(n||K(e)){let r=n?[]:{};for(let i in e)r[i]=me(Oe(e[i],`${t+i}.`));let s=me(0);return new Proxy(r,{get(i,o){if(!(o==="toJSON"&&!x(r,o)))return n&&o in Array.prototype?(s(),r[o]):typeof o=="symbol"?r[o]:((!x(r,o)||r[o]()==null)&&(r[o]=me(""),z(t+o,""),s(s()+1)),r[o]())},set(i,o,a){let c=t+o;if(n&&o==="length"){let l=r[o]-a;if(r[o]=a,l>0){let u={};for(let f=a;f{if(e!==void 0&&t!==void 0&&xe.push([e,t]),!He&&xe.length){let n=Le(xe);xe.length=0,document.dispatchEvent(new CustomEvent(ne,{detail:n}))}},D=(e,{ifMissing:t}={})=>{N();for(let n in e)e[n]==null?t||delete se[n]:Pt(e[n],n,se,"",t);P()},S=(e,t)=>D(Le(e),t),Pt=(e,t,n,r,s)=>{if(K(e)){x(n,t)&&(K(n[t])||Array.isArray(n[t]))||(n[t]={});for(let i in e)e[i]==null?s||delete n[t][i]:Pt(e[i],i,n[t],`${r+t}.`,s)}else s&&x(n,t)||(n[t]=e)},St=e=>typeof e=="string"?RegExp(e.replace(/^\/|\/$/g,"")):e,$=({include:e=/.*/,exclude:t=/(?!)/}={},n=se)=>{let r=St(e),s=St(t),i=[],o=[[n,""]];for(;o.length;){let[a,c]=o.pop();for(let l in a){let u=c+l;K(a[l])?o.push([a[l],`${u}.`]):r.test(u)&&!s.test(u)&&i.push([u,W(u)])}}return Le(i)},se=Oe({});var Z=e=>e instanceof HTMLElement||e instanceof SVGElement||e instanceof MathMLElement;var ge=e=>e.replace(/([A-Z]+)([A-Z][a-z])/g,"$1-$2").replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([a-z])([0-9]+)/gi,"$1-$2").replace(/([0-9]+)([a-z])/gi,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase();var Ot=e=>ge(e).replace(/-/g,"_");var dn=/^(?:(?:async\s+)?function\b|(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>)/,ae=(e,t={})=>{let{reviveFunctionStrings:n=!1}=t;try{return n?JSON.parse(e,(r,s)=>{if(typeof s!="string")return s;let i=s.trim();if(!dn.test(i))return s;try{let o=Function(`return (${i})`)();return typeof o=="function"?o:s}catch{return s}}):JSON.parse(e)}catch{return Function(`return (${e})`)()}},Ct={camel:e=>e.replace(/-[a-z]/g,t=>t[1].toUpperCase()),snake:e=>e.replace(/-/g,"_"),pascal:e=>e[0].toUpperCase()+Ct.camel(e.slice(1))},O=(e,t,n="camel")=>{for(let r of t.get("case")||[n])e=Ct[r]?.(e)||e;return e},U=e=>`data-${e}`,tt=e=>e;var pn="https://data-star.dev/errors",he=(e,t,n={})=>{Object.assign(n,e);let r=new Error,s=Ot(t),i=new URLSearchParams({metadata:JSON.stringify(n)}).toString(),o=JSON.stringify(n,null,2);return r.message=`${t} +More info: ${pn}/${s}?${i} +Context: ${o}`,r},ye=new Map,nt=new Map,_t=new Map,kt=new Proxy({},{get:(e,t)=>ye.get(t)?.apply,has:(e,t)=>ye.has(t),ownKeys:()=>Reflect.ownKeys(ye),set:()=>!1,deleteProperty:()=>!1}),be=new Map,ke=[],rt=new Set,ve=new Set,Ft=!1,g=e=>{ke.push(e),ke.length===1&&setTimeout(()=>{for(let n of ke)rt.add(n.name),nt.set(n.name,n);ke.length=0;let t=ve.size?[...ve]:[document.documentElement];for(let n of t)En(n,!ve.has(n));rt.clear()})},V=e=>{ye.set(e.name,e)};document.addEventListener(B,e=>{let t=_t.get(e.detail.type);t&&t.apply({error:he.bind(0,{plugin:{type:"watcher",name:t.name},element:{id:e.target.id,tag:e.target.tagName}})},e.detail.argsRaw)});var Ee=e=>{_t.set(e.name,e)},Ht=e=>{for(let t of e){let n=be.get(t);if(n&&be.delete(t))for(let r of n.values())for(let s of r.values())s()}},Dt=U("ignore"),mn=`[${Dt}]`,Vt=e=>e.hasAttribute(`${Dt}__self`)||!!e.closest(mn),De=(e,t)=>{for(let n of e)if(!Vt(n)){let r=new Set;for(let s in n.dataset){let i=s.replace(/[A-Z]/g,"-$&").toLowerCase();r.add(i),st(n,i,n.dataset[s],t)}for(let s of Array.from(n.attributes)){if(!s.name.startsWith("data-"))continue;let i=s.name.slice(5);r.has(i)||st(n,i,s.value,t)}}},gn=e=>{for(let{target:t,type:n,attributeName:r,addedNodes:s,removedNodes:i}of e)if(n==="childList"){for(let o of i)Z(o)&&(Ht([o]),Ht(o.querySelectorAll("*")));for(let o of s)Z(o)&&(De([o]),De(o.querySelectorAll("*")))}else if(n==="attributes"&&r.startsWith("data-")&&Z(t)&&!Vt(t)){let o=r.slice(5),a=tt(o);if(!a)continue;let c=t.getAttribute(r);if(c===null){let l=be.get(t);if(l){let u=l.get(a);if(u){for(let f of u.values())f();l.delete(a)}}}else st(t,o,c)}},hn=new MutationObserver(gn),yn=e=>{let[t,...n]=e.split("__"),[r,s]=t.split(/:(.+)/),i=new Map;for(let o of n){let[a,...c]=o.split(".");i.set(a,new Set(c))}return{pluginName:r,key:s,mods:i}},vn=()=>ve.has(document.documentElement),bn=()=>{Ft||!vn()||(Ft=!0,document.dispatchEvent(new Event(vt)))},En=(e=document.documentElement,t=!0)=>{Z(e)&&De([e],!0),De(e.querySelectorAll("*"),!0),t&&(hn.observe(e,{subtree:!0,childList:!0,attributes:!0}),ve.add(e),bn())};var st=(e,t,n,r)=>{let s=tt(t);if(!s)return;let{pluginName:i,key:o,mods:a}=yn(s),c=nt.get(i);if((!r||rt.has(i))&&!!c){let u={el:e,rawKey:s,mods:a,error:he.bind(0,{plugin:{type:"attribute",name:c.name},element:{id:e.id,tag:e.tagName},expression:{rawKey:s,key:o,value:n}}),key:o,value:n,loadedPluginNames:{actions:new Set(ye.keys()),attributes:new Set(nt.keys())},rx:void 0},f=c.requirement&&(typeof c.requirement=="string"?c.requirement:c.requirement.key)||"allowed",h=c.requirement&&(typeof c.requirement=="string"?c.requirement:c.requirement.value)||"allowed",d=o!=null&&o!=="",p=n!=null&&n!=="";if(d){if(f==="denied")throw u.error("KeyNotAllowed")}else if(f==="must")throw u.error("KeyRequired");if(p){if(h==="denied")throw u.error("ValueNotAllowed")}else if(h==="must")throw u.error("ValueRequired");if(f==="exclusive"||h==="exclusive"){if(d&&p)throw u.error("KeyAndValueProvided");if(!d&&!p)throw u.error("KeyOrValueRequired")}let m=new Map;if(p){let v;u.rx=(...F)=>(v||(v=Sn(n,{returnsValue:c.returnsValue,argNames:c.argNames,cleanups:m})),v(e,...F))}let y=c.apply(u);y&&m.set("attribute",y);let T=be.get(e);if(T){let v=T.get(s);if(v)for(let F of v.values())F()}else T=new Map,be.set(e,T);T.set(s,m)}},Sn=(e,{returnsValue:t=!1,argNames:n=[],cleanups:r=new Map}={})=>{let s="";if(t){let c=/(\/(\\\/|[^/])*\/|"(\\"|[^"])*"|'(\\'|[^'])*'|`(\\`|[^`])*`|\(\s*((function)\s*\(\s*\)|(\(\s*\))\s*=>)\s*(?:\{[\s\S]*?\}|[^;){]*)\s*\)\s*\(\s*\)|[^;])+/gm,l=e.trim().match(c);if(l){let u=l.length-1,f=l[u].trim();f.startsWith("return")||(l[u]=`return (${f});`),s=l.join(`; +`)}}else s=e.trim();let i=new Map,o=RegExp(`(?:${Ue})(.*?)(?:${Je})`,"gm"),a=0;for(let c of s.matchAll(o)){let l=c[1],u=`__escaped${a++}`;i.set(u,l),s=s.replace(Ue+l+Je,u)}s=s.replace(/("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\$]|\$(?!\{))*`)|\$\{([^{}]*)\}|\$([a-zA-Z_\d]\w*(?:[.-]\w+)*)/g,(c,l,u,f)=>l?c:u!==void 0?`\${${u.replace(/\$([a-zA-Z_\d]\w*(?:[.-]\w+)*)/g,(h,d)=>d.split(".").reduce((p,m)=>`${p}['${m}']`,"$"))}}`:f.split(".").reduce((h,d)=>`${h}['${d}']`,"$")),s=s.replaceAll(/@([A-Za-z_$][\w$]*)\(/g,'__action("$1",evt,');for(let[c,l]of i)s=s.replace(c,l);try{let c=Function("el","$","__action","evt",...n,s);return(l,...u)=>{let f=(h,d,...p)=>{let m=he.bind(0,{plugin:{type:"action",name:h},element:{id:l.id,tag:l.tagName},expression:{fnContent:s,value:e}}),y=kt[h];if(y)return y({el:l,evt:d,error:m,cleanups:r},...p);throw m("UndefinedAction")};try{return c(l,se,f,void 0,...u)}catch(h){throw console.error(h),he({element:{id:l.id,tag:l.tagName},expression:{fnContent:s,value:e},error:h.message},"ExecuteExpression")}}}catch(c){throw console.error(c),he({expression:{fnContent:s,value:e},error:c.message},"GenerateExpression")}};V({name:"peek",apply(e,t){_();try{return t()}finally{k()}}});V({name:"setAll",apply(e,t,n){_();let r=$(n);re(r,()=>t),D(r),k()}});V({name:"toggleAll",apply(e,t){_();let n=$(t);re(n,r=>!r),D(n),k()}});var It=new WeakMap,it=e=>!["GET","DELETE"].includes(e),Se=(e,t,n=!0)=>V({name:e,apply:async({el:r,evt:s,error:i,cleanups:o},a,{selector:c,headers:l,contentType:u="json",filterSignals:{include:f=/.*/,exclude:h=/(^|\.)_/}={},openWhenHidden:d=n,payload:p,requestCancellation:m="auto",retry:y="auto",retryInterval:T=1e3,retryScaler:v=2,retryMaxWait:F=3e4,retryMaxCount:Re=10}={})=>{let X=m instanceof AbortController?m:new AbortController;(m==="auto"||m==="cleanup")&&(It.get(r)?.abort(),It.set(r,X)),m==="cleanup"&&(o.get(`@${e}`)?.(),o.set(`@${e}`,async()=>{X.abort(),await Promise.resolve()}));let ee=()=>{};try{if(!a?.length)throw i("FetchNoUrlProvided",{action:V});let fe={Accept:"text/event-stream, text/html, application/json","Datastar-Request":!0};u==="json"&&it(t)&&(fe["Content-Type"]="application/json");let q=Object.assign({},fe,l),C={input:"",method:t,headers:q,openWhenHidden:d,retry:y,retryInterval:T,retryScaler:v,retryMaxWait:F,retryMaxCount:Re,signal:X.signal,onopen:async b=>{b.status>=400&&ie(Tn,r,{status:b.status.toString()})},onmessage:b=>{if(!b.event.startsWith("datastar"))return;let J=b.event,w={};for(let E of b.data.split(` +`)){let A=E.indexOf(" "),H=E.slice(0,A),M=E.slice(A+1);(w[H]||=[]).push(M)}let L=Object.fromEntries(Object.entries(w).map(([E,A])=>[E,A.join(` +`)]));ie(J,r,L)},onerror:b=>{if($t(b))throw b("FetchExpectedTextEventStream",{url:a});b&&(console.error(b.message),ie(An,r,{message:b.message}))}},qe=()=>{let b=new URL(a,document.baseURI),J=new URLSearchParams(b.search);if(u==="json"){_();let w=p!==void 0?p:$({include:f,exclude:h});k();let L=JSON.stringify(w);it(t)?C.body=L:J.set("datastar",L)}else if(u==="form"){let w=c?document.querySelector(c):r.closest("form");if(!w)throw i("FetchFormNotFound",{action:V,selector:c});if(!w.noValidate&&!w.checkValidity()){w.reportValidity();return}let L=new FormData(w),E=r;if(r===w&&s instanceof SubmitEvent)E=s.submitter;else{let M=de=>de.preventDefault();w.addEventListener("submit",M),ee=()=>{w.removeEventListener("submit",M)}}if(E instanceof HTMLButtonElement||E instanceof HTMLInputElement&&E.type==="submit"){let M=E.getAttribute("name");M&&L.append(M,E.value)}let A=w.getAttribute("enctype")==="multipart/form-data";A||(q["Content-Type"]="application/x-www-form-urlencoded");let H=new URLSearchParams(L);if(it(t))A?C.body=L:C.body=H;else for(let[M,de]of H)J.append(M,de)}else throw i("FetchInvalidContentType",{action:V,contentType:u});return b.search=J.toString(),C.input=b.toString(),C};ie(ot,r,{});try{await Nn(r,qe)}catch(b){if(!$t(b))throw i("FetchFailed",{method:t,url:a,error:b.message})}}finally{ie(at,r,{}),ee(),o.delete(`@${e}`)}}});Se("get","GET",!1);Se("patch","PATCH");Se("post","POST");Se("put","PUT");Se("delete","DELETE");var ot="started",at="finished",Tn="error",An="retrying",Rn="retries-failed",ie=(e,t,n)=>document.dispatchEvent(new CustomEvent(B,{detail:{type:e,el:t,argsRaw:n}})),$t=e=>`${e}`.includes("text/event-stream"),wn=async(e,t)=>{let n=e.getReader(),r=await n.read();for(;!r.done;)t(r.value),r=await n.read()},Mn=e=>{let t,n,r,s=!1;return i=>{t?t=xn(t,i):(t=i,n=0,r=-1);let o=t.length,a=0;for(;n{let r=qt(),s=new TextDecoder;return(i,o)=>{if(!i.length)n?.(r),r=qt();else if(o>0){let a=s.decode(i.subarray(0,o)),c=o+(i[o+1]===32?2:1),l=s.decode(i.subarray(c));switch(a){case"data":r.data=r.data?`${r.data} +${l}`:l;break;case"event":r.event=l;break;case"id":e(r.id=l);break;case"retry":{let u=+l;Number.isNaN(u)||t(r.retry=u);break}}}}},xn=(e,t)=>{let n=new Uint8Array(e.length+t.length);return n.set(e),n.set(t,e.length),n},qt=()=>({data:"",event:"",id:"",retry:void 0}),Nn=(e,t)=>new Promise((n,r)=>{let s=t();if(!s)return;let{input:i,signal:o,headers:a,onopen:c,onmessage:l,onclose:u,onerror:f,openWhenHidden:h,fetch:d,retry:p="auto",retryInterval:m=1e3,retryScaler:y=2,retryMaxWait:T=3e4,retryMaxCount:v=10,responseOverrides:F,...Re}=s,X={...a},ee,fe=()=>{if(ee.abort(),!document.hidden){let E=t();if(!E)return;i=E.input,Re.body=E.body,L()}};h||document.addEventListener("visibilitychange",fe);let q,C=()=>{document.removeEventListener("visibilitychange",fe),clearTimeout(q),ee.abort()};o?.addEventListener("abort",()=>{C(),n()});let qe=d||window.fetch,b=c||(()=>{}),J=0,w=m,L=async()=>{ee=new AbortController;let E=ee.signal;try{let A=await qe(i,{...Re,headers:X,signal:E});await b(A);let H=async(G,pe,Be,we,...an)=>{let ht={[Be]:await pe.text()};for(let je of an){let We=pe.headers.get(`datastar-${ge(je)}`);if(we){let Me=we[je];Me&&(We=typeof Me=="string"?Me:JSON.stringify(Me))}We&&(ht[je]=We)}ie(G,e,ht),C(),n()},M=A.status,de=M===204,gt=M>=300&&M<400,on=M>=400&&M<600;if(M!==200){if(u?.(),p!=="never"&&!de&&!gt&&(p==="always"||p==="error"&&on)){clearTimeout(q),q=setTimeout(L,m);return}C(),n();return}J=0,m=w;let Ge=A.headers.get("Content-Type");if(Ge?.includes("text/html"))return await H("datastar-patch-elements",A,"elements",F,"selector","mode","namespace","useViewTransition");if(Ge?.includes("application/json"))return await H("datastar-patch-signals",A,"signals",F,"onlyIfMissing");if(Ge?.includes("text/javascript")){let G=document.createElement("script"),pe=A.headers.get("datastar-script-attributes");if(pe)for(let[Be,we]of Object.entries(JSON.parse(pe)))G.setAttribute(Be,we);G.textContent=await A.text(),document.head.appendChild(G),C();return}if(await wn(A.body,Mn(Ln(G=>{G?X["last-event-id"]=G:delete X["last-event-id"]},G=>{w=m=G},l))),u?.(),p==="always"&&!gt){clearTimeout(q),q=setTimeout(L,m);return}C(),n()}catch(A){if(!E.aborted)try{let H=f?.(A)||m;clearTimeout(q),q=setTimeout(L,H),m=Math.min(m*y,T),++J>=v?(ie(Rn,e,{}),C(),r("Max retries reached.")):console.error(`Datastar failed to reach ${i.toString()} retrying in ${H}ms.`)}catch(H){C(),r(H)}}};L()});g({name:"attr",requirement:{value:"must"},returnsValue:!0,apply({el:e,key:t,rx:n}){let r=(a,c)=>{c===""||c===!0?e.setAttribute(a,""):c===!1||c==null?e.removeAttribute(a):typeof c=="string"?e.setAttribute(a,c):typeof c=="function"?e.setAttribute(a,c.toString()):e.setAttribute(a,JSON.stringify(c,(l,u)=>typeof u=="function"?u.toString():u))},s=t?()=>{i.disconnect();let a=n();r(t,a),i.observe(e,{attributeFilter:[t]})}:()=>{i.disconnect();let a=n(),c=Object.keys(a);for(let l of c)r(l,a[l]);i.observe(e,{attributeFilter:c})},i=new MutationObserver(s),o=R(s);return()=>{i.disconnect(),o()}}});var Ve=(e,...t)=>({get:n=>n[e],set:(n,r)=>{n[e]=r},events:t}),Gt=(e,...t)=>({get:n=>n.getAttribute(e),set:(n,r)=>{n.setAttribute(e,`${r}`)},events:t}),ct=(e=!1,...t)=>({get:(n,r)=>r==="string"||e&&r==="undefined"?n.value:+n.value,set:(n,r)=>{n.value=`${r}`},events:t}),Pn=/^data:(?[^;]+);base64,(?.*)$/,lt=Symbol("empty"),Ie=U("bind"),Bt=(e,t,n,r,s,i)=>{if(i===void 0&&e instanceof HTMLInputElement&&e.type==="radio"){let u=t||n,f=[...document.querySelectorAll(`[${Ie}\\:${CSS.escape(u)}],[${Ie}="${CSS.escape(u)}"]`)].find(h=>h instanceof HTMLInputElement&&h.checked);f&&S([[r,f.value]],{ifMissing:!0})}if(!Array.isArray(i)||e instanceof HTMLSelectElement&&e.multiple)return S([[r,s.get(e,typeof i)]],{ifMissing:!0}),r;let o=t||n,a=document.querySelectorAll(`[${Ie}\\:${CSS.escape(o)}],[${Ie}="${CSS.escape(o)}"]`),c=[],l=0;for(let u of a){if(c.push([`${r}.${l}`,s.get(u,typeof(x(i,l)?i[l]:void 0))]),e===u)break;l++}return S(c,{ifMissing:!0}),`${r}.${l}`};g({name:"bind",requirement:"exclusive",apply({el:e,key:t,mods:n,value:r,error:s}){let i=t!=null?O(t,n):r,o=n.get("prop"),a=n.get("event"),c=null;if(o){let d=[...o][0];if(!d?.length)throw s("BindPropNameMissing");if(!a?.size)throw s("BindEventRequired");c=Ve(d,...a)}else if(a)throw s("BindPropRequiredWhenEventProvided");if(c){let d=W(i),p=Bt(e,t,r,i,c,d),m=()=>{let T=W(p);if(T!=null){let v=c?.get(e,typeof T);v!==lt&&S([[p,v]])}};for(let T of c?.events??[])e.addEventListener(T,m);e.addEventListener(te,m);let y=R(()=>{c?.set(e,W(p))});return()=>{y();for(let T of c?.events??[])e.removeEventListener(T,m);e.removeEventListener(te,m)}}if(e instanceof HTMLInputElement)switch(e.type){case"range":case"number":c=ct(!1,"input");break;case"checkbox":c={get:(d,p)=>d.value!=="on"?p==="boolean"?d.checked:d.checked?d.value:"":p==="string"?d.checked?d.value:"":d.checked,set:(d,p)=>{d.checked=typeof p=="string"?p===d.value:p},events:["change"]};break;case"radio":e.getAttribute("name")?.length||e.setAttribute("name",i),c={get:(d,p)=>d.checked?p==="number"?+d.value:d.value:lt,set:(d,p)=>{d.checked=p===(typeof p=="number"?+d.value:d.value)},events:["change"]};break;case"file":{let d=()=>{let p=[...e.files||[]],m=[];Promise.all(p.map(y=>new Promise(T=>{let v=new FileReader;v.onload=()=>{if(typeof v.result!="string")throw s("InvalidFileResultType",{resultType:typeof v.result});let F=v.result.match(Pn);if(!F?.groups)throw s("InvalidDataUri",{result:v.result});m.push({name:y.name,contents:F.groups.contents,mime:F.groups.mime})},v.onloadend=()=>T(),v.readAsDataURL(y)}))).then(()=>{S([[i,m]])})};return e.addEventListener("change",d),()=>{e.removeEventListener("change",d)}}default:c=ct(!0,"input")}else if(e instanceof HTMLSelectElement&&e.multiple){let d=new Map;c={get:p=>[...p.selectedOptions].map(m=>{let y=d.get(m.value);return y==="string"||y==null?m.value:+m.value}),set:(p,m)=>{for(let y of p.options)m.includes(y.value)?(d.set(y.value,"string"),y.selected=!0):m.includes(+y.value)?(d.set(y.value,"number"),y.selected=!0):y.selected=!1},events:["change"]}}else e instanceof HTMLSelectElement?c=ct(!1,"change"):e instanceof HTMLTextAreaElement?c=Ve("value","input"):e instanceof HTMLElement&&e.tagName.includes("-")?c="value"in e?Ve("value","input","change"):Gt("value","input","change"):e instanceof HTMLElement&&"value"in e?c=Ve("value","change"):c=Gt("value","change");if(!c)throw s("InvalidBindAdapter");let l=W(i),u=Bt(e,t,r,i,c,l),f=()=>{let d=W(u);if(d!=null){let p=c.get(e,typeof d);p!==lt&&S([[u,p]])}};for(let d of c.events)e.addEventListener(d,f);e.addEventListener(te,f);let h=R(()=>{c.set(e,W(u))});return()=>{h();for(let d of c.events)e.removeEventListener(d,f);e.removeEventListener(te,f)}}});g({name:"class",requirement:{value:"must"},returnsValue:!0,apply({key:e,el:t,mods:n,rx:r}){e&&=O(e,n,"kebab");let s,i=()=>{o.disconnect(),s=e?{[e]:r()}:r();for(let c in s){let l=c.split(/\s+/).filter(u=>u.length>0);if(s[c])for(let u of l)t.classList.contains(u)||t.classList.add(u);else for(let u of l)t.classList.contains(u)&&t.classList.remove(u)}o.observe(t,{attributeFilter:["class"]})},o=new MutationObserver(i),a=R(i);return()=>{o.disconnect(),a();for(let c in s){let l=c.split(/\s+/).filter(u=>u.length>0);for(let u of l)t.classList.remove(u)}}}});g({name:"computed",requirement:{value:"must"},returnsValue:!0,apply({key:e,mods:t,rx:n,error:r}){if(e)S([[O(e,t),_e(n)]]);else{let s=Object.assign({},n());re(s,i=>{if(typeof i=="function")return _e(i);throw r("ComputedExpectedFunction")}),D(s)}}});g({name:"effect",requirement:{key:"denied",value:"must"},apply:({rx:e})=>R(e)});g({name:"indicator",requirement:"exclusive",apply({el:e,key:t,mods:n,value:r}){let s=t!=null?O(t,n):r,i=0;S([[s,!1]]);let o=a=>{let{type:c,el:l}=a.detail;if(l===e)switch(c){case ot:i++,S([[s,!0]]);break;case at:i=Math.max(0,i-1),S([[s,i>0]]);break}};return document.addEventListener(B,o),()=>{i=0,S([[s,!1]]),document.removeEventListener(B,o)}}});var Q=e=>{if(!e||e.size<=0)return 0;for(let t of e){if(t.endsWith("ms"))return+t.replace("ms","");if(t.endsWith("s"))return+t.replace("s","")*1e3;try{return Number.parseFloat(t)}catch{}}return 0},oe=(e,t,n=!1)=>e?e.has(t.toLowerCase()):n,jt=(e,t="")=>{if(e&&e.size>0)for(let n of e)return n;return t};var ut=(e,t)=>(...n)=>{setTimeout(()=>{e(...n)},t)},Wt=(e,t,n=!0,r=!1,s=!1)=>{let i=null,o=0;return(...a)=>{n&&!o?(e(...a),i=null):i=a,(!o||s)&&(o&&clearTimeout(o),o=setTimeout(()=>{r&&i!==null&&e(...i),i=null,o=0},t))}},ce=(e,t)=>{let n=t.get("delay");if(n){let i=Q(n);e=ut(e,i)}let r=t.get("debounce");if(r){let i=Q(r),o=oe(r,"leading",!1),a=!oe(r,"notrailing",!1);e=Wt(e,i,o,a,!0)}let s=t.get("throttle");if(s){let i=Q(s),o=!oe(s,"noleading",!1),a=oe(s,"trailing",!1);e=Wt(e,i,o,a)}return e};var ft=!!document.startViewTransition,Y=(e,t)=>{if(t.has("viewtransition")&&ft){let n=e;e=(...r)=>document.startViewTransition(()=>n(...r))}return e};g({name:"init",requirement:{key:"denied",value:"must"},apply({rx:e,mods:t}){let n=()=>{N(),e(),P()};n=Y(n,t);let r=0,s=t.get("delay");s&&(r=Q(s),r>0&&(n=ut(n,r))),n()}});g({name:"json-signals",requirement:{key:"denied"},apply({el:e,value:t,mods:n}){let r=n.has("terse")?0:2,s={};t&&(s=ae(t));let i=()=>{o.disconnect(),e.textContent=JSON.stringify($(s),null,r),o.observe(e,{childList:!0,characterData:!0,subtree:!0})},o=new MutationObserver(i),a=R(i);return()=>{o.disconnect(),a()}}});g({name:"on",requirement:"must",argNames:["evt"],apply({el:e,key:t,mods:n,rx:r}){let s=e;n.has("window")?s=window:n.has("document")&&(s=document);let i=l=>{N(),r(l),P()};i=Y(i,n),i=ce(i,n);let o=O(t,n,"kebab"),a={capture:n.has("capture"),passive:n.has("passive"),once:n.has("once")};if(n.has("outside")){s=document;let l=i;i=u=>{e.contains(u?.target)||l(u)}}(o===B||o===ne)&&(s=document);let c=l=>{l&&(n.has("prevent")&&l.preventDefault(),n.has("stop")&&l.stopPropagation(),e instanceof HTMLFormElement&&o==="submit"&&l.preventDefault()),i(l)};return s.addEventListener(o,c,a),()=>{s.removeEventListener(o,c,a)}}});var Ut=(e,t,n)=>Math.max(t,Math.min(n,e));var dt=new WeakSet;g({name:"on-intersect",requirement:{key:"denied",value:"must"},apply({el:e,mods:t,rx:n}){let r=()=>{N(),n(),P()};r=Y(r,t),r=ce(r,t);let s={threshold:0};if(t.has("full"))s.threshold=1;else if(t.has("half"))s.threshold=.5;else{let a=t.get("threshold");a&&(s.threshold=Ut(Number(jt(a)),0,100)/100)}let i=t.has("exit"),o=new IntersectionObserver(a=>{for(let c of a)c.isIntersecting!==i&&(r(),o&&dt.has(e)&&o.disconnect())},s);return o.observe(e),t.has("once")&&dt.add(e),()=>{t.has("once")||dt.delete(e),o&&(o.disconnect(),o=null)}}});g({name:"on-interval",requirement:{key:"denied",value:"must"},apply({mods:e,rx:t}){let n=()=>{N(),t(),P()};n=Y(n,e);let r=1e3,s=e.get("duration");s&&(r=Q(s),oe(s,"leading",!1)&&n());let i=setInterval(n,r);return()=>{clearInterval(i)}}});g({name:"on-signal-patch",requirement:{value:"must"},argNames:["patch"],returnsValue:!0,apply({el:e,key:t,mods:n,rx:r,error:s}){if(t&&t!=="filter")throw s("KeyNotAllowed");let i=U(`${this.name}-filter`),o=e.getAttribute(i),a={};o&&(a=ae(o));let c=!1,l=ce(u=>{if(c)return;let f=$(a,u.detail);if(!bt(f)){c=!0,N();try{r(f)}finally{P(),c=!1}}},n);return document.addEventListener(ne,l),()=>{document.removeEventListener(ne,l)}}});g({name:"ref",requirement:"exclusive",apply({el:e,key:t,mods:n,value:r}){let s=t!=null?O(t,n):r;S([[s,e]])}});var Jt="none",Kt="display";g({name:"show",requirement:{key:"denied",value:"must"},returnsValue:!0,apply({el:e,rx:t}){let n=()=>{r.disconnect(),t()?e.style.display===Jt&&e.style.removeProperty(Kt):e.style.setProperty(Kt,Jt),r.observe(e,{attributeFilter:["style"]})},r=new MutationObserver(n),s=R(n);return()=>{r.disconnect(),s()}}});g({name:"signals",returnsValue:!0,apply({key:e,mods:t,rx:n}){let r=t.has("ifmissing");if(e){e=O(e,t);let s=n?.();S([[e,s]],{ifMissing:r})}else{let s=Object.assign({},n?.());D(s,{ifMissing:r})}}});g({name:"style",requirement:{value:"must"},returnsValue:!0,apply({key:e,el:t,rx:n}){let{style:r}=t,s=new Map,i=(l,u)=>{let f=s.get(l);!u&&u!==0?f!==void 0&&(f?r.setProperty(l,f):r.removeProperty(l)):(f===void 0&&s.set(l,r.getPropertyValue(l)),r.setProperty(l,String(u)))},o=()=>{if(a.disconnect(),e)i(e,n());else{let l=n();for(let[u,f]of s)u in l||(f?r.setProperty(u,f):r.removeProperty(u));for(let u in l)i(ge(u),l[u])}a.observe(t,{attributeFilter:["style"]})},a=new MutationObserver(o),c=R(o);return()=>{a.disconnect(),c();for(let[l,u]of s)u?r.setProperty(l,u):r.removeProperty(l)}}});g({name:"text",requirement:{key:"denied",value:"must"},returnsValue:!0,apply({el:e,rx:t}){let n=()=>{r.disconnect(),e.textContent=`${t()}`,r.observe(e,{childList:!0,characterData:!0,subtree:!0})},r=new MutationObserver(n),s=R(n);return()=>{r.disconnect(),s()}}});var zt=(e,t)=>e.includes(t),On=["remove","outer","inner","replace","prepend","append","before","after"],Cn=["html","svg","mathml"];Ee({name:"datastar-patch-elements",apply(e,t){let n=typeof t.selector=="string"?t.selector:"",r=typeof t.mode=="string"?t.mode:"outer",s=typeof t.namespace=="string"?t.namespace:"html",i=typeof t.useViewTransition=="string"?t.useViewTransition:"",o=t.elements;if(!zt(On,r))throw e.error("PatchElementsInvalidMode",{mode:r});if(!n&&r!=="outer"&&r!=="replace")throw e.error("PatchElementsExpectedSelector");if(!zt(Cn,s))throw e.error("PatchElementsInvalidNamespace",{namespace:s});let a={selector:n,mode:r,namespace:s,useViewTransition:i.trim()==="true",elements:o};ft&&a.useViewTransition?document.startViewTransition(()=>Zt(e,a)):Zt(e,a)}});var Zt=({error:e},{selector:t,mode:n,namespace:r,elements:s})=>{let i=document.createDocumentFragment(),o=typeof s!="string"&&!!s;if(typeof s=="string"){let a=s.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,""),c=/<\/html>/.test(a),l=/<\/head>/.test(a),u=/<\/body>/.test(a),f=r==="svg"?"svg":r==="mathml"?"math":"",h=f?`<${f}>${s}`:s,d=new DOMParser().parseFromString(c||l||u?s:``,"text/html");if(c)i.appendChild(d.documentElement);else if(l&&u)i.appendChild(d.head),i.appendChild(d.body);else if(l)i.appendChild(d.head);else if(u)i.appendChild(d.body);else if(f){let p=d.querySelector("template").content.querySelector(f);for(let m of p.childNodes)i.appendChild(m)}else i=d.querySelector("template").content}else s&&(s instanceof DocumentFragment?i=s:s instanceof Element&&i.appendChild(s));if(!t&&(n==="outer"||n==="replace")){let a=Array.from(i.children);for(let c of a){let l;if(c instanceof HTMLHtmlElement)l=document.documentElement;else if(c instanceof HTMLBodyElement)l=document.body;else if(c instanceof HTMLHeadElement)l=document.head;else if(l=document.getElementById(c.id),!l){console.warn(e("PatchElementsNoTargetsFound"),{element:{id:c.id}});continue}Yt(n,c,[l],o)}}else{let a=document.querySelectorAll(t);if(!a.length){console.warn(e("PatchElementsNoTargetsFound"),{selector:t});return}let c=o&&n!=="remove"?[a[0]]:a;Yt(n,i,c,o)}},mt=new WeakSet;for(let e of document.querySelectorAll("script"))mt.add(e);var nn=e=>{let t=e instanceof HTMLScriptElement?[e]:e.querySelectorAll("script");for(let n of t)if(!mt.has(n)){let r=document.createElement("script");for(let{name:s,value:i}of n.attributes)r.setAttribute(s,i);r.text=n.text,n.replaceWith(r),mt.add(r)}},Qt=(e,t,n,r)=>{let s=!1;for(let i of e){if(r&&s)break;let o=r?t:t.cloneNode(!0);nn(o),i[n](o),s=!0}},Yt=(e,t,n,r)=>{switch(e){case"remove":for(let s of n)s.remove();break;case"outer":case"inner":{let s=!1;for(let i of n){if(r&&s)break;let o=r?t:t.cloneNode(!0);Hn(i,o,e),nn(i);let a=i.closest("[data-scope-children]");a&&a.dispatchEvent(new CustomEvent(Ke,{bubbles:!1})),s=!0}}break;case"replace":Qt(n,t,"replaceWith",r);break;case"prepend":case"append":case"before":case"after":Qt(n,t,e,r)}},I=new Map,ue=new Set,le=new Map,Te=new Set,$e=document.createElement("div");$e.hidden=!0;var Ae=U("ignore-morph"),Fn=`[${Ae}]`,Hn=(e,t,n="outer")=>{if(Z(e)&&Z(t)&&e.hasAttribute(Ae)&&t.hasAttribute(Ae)||e.parentElement?.closest(Fn))return;let r=document.createElement("div");r.append(t),document.body.insertAdjacentElement("afterend",$e);let s=e.querySelectorAll("[id]");for(let{id:a,tagName:c}of s)le.has(a)?Te.add(a):le.set(a,c);e instanceof Element&&e.id&&(le.has(e.id)?Te.add(e.id):le.set(e.id,e.tagName)),ue.clear();let i=r.querySelectorAll("[id]");for(let{id:a,tagName:c}of i)ue.has(a)?Te.add(a):le.get(a)===c&&ue.add(a);for(let a of Te)ue.delete(a);le.clear(),Te.clear(),I.clear();let o=n==="outer"?e.parentElement:e;tn(o,s),tn(r,i),rn(o,r,n==="outer"?e:null,e.nextSibling),$e.remove()},rn=(e,t,n=null,r=null)=>{e instanceof HTMLTemplateElement&&t instanceof HTMLTemplateElement&&(e=e.content,t=t.content),n??=e.firstChild;for(let s of t.childNodes){if(n&&n!==r){let i=_n(s,n,r);if(i){if(i!==n){let o=n;for(;o&&o!==i;){let a=o;o=o.nextSibling,en(a)}}pt(i,s),n=i.nextSibling;continue}}if(s instanceof Element&&ue.has(s.id)){let i=document.getElementById(s.id),o=i;for(;o=o.parentNode;){let a=I.get(o);a&&(a.delete(s.id),a.size||I.delete(o))}sn(e,i,n),pt(i,s),n=i.nextSibling;continue}if(I.has(s)){let i=s.namespaceURI,o=s.tagName,a=i&&i!=="http://www.w3.org/1999/xhtml"?document.createElementNS(i,o):document.createElement(o);e.insertBefore(a,n),pt(a,s),n=a.nextSibling}else{let i=document.importNode(s,!0);e.insertBefore(i,n),n=i.nextSibling}}for(;n&&n!==r;){let s=n;n=n.nextSibling,en(s)}},_n=(e,t,n)=>{let r=null,s=e.nextSibling,i=0,o=0,a=I.get(e)?.size||0,c=t;for(;c&&c!==n;){if(Xt(c,e)){let l=!1,u=I.get(c),f=I.get(e);if(f&&u){for(let h of u)if(f.has(h)){l=!0;break}}if(l)return c;if(!r&&!I.has(c)){if(!a)return c;r=c}}if(o+=I.get(c)?.size||0,o>a)break;r===null&&s&&Xt(c,s)&&(i++,s=s.nextSibling,i>=2&&(r=void 0)),c=c.nextSibling}return r||null},Xt=(e,t)=>e.nodeType===t.nodeType&&e.tagName===t.tagName&&(!e.id||e.id===t.id),en=e=>{I.has(e)?sn($e,e,null):e.parentNode?.removeChild(e)},sn=(e,t,n)=>{if("moveBefore"in e){e.moveBefore(t,n);return}e.insertBefore(t,n)},kn=U("preserve-attr"),pt=(e,t)=>{let n=t.nodeType;if(n===1){let r=e,s=t,i=r.hasAttribute("data-scope-children");if(r.hasAttribute(Ae)&&s.hasAttribute(Ae))return e;let o=(t.getAttribute(kn)??"").split(" "),a=(l,u,f)=>{let h=u.hasAttribute(f);return l.hasAttribute(f)!==h&&!o.includes(f)?(l[f]=h,!0):!1},c=!1;if(r instanceof HTMLInputElement&&s instanceof HTMLInputElement&&s.type!=="file"){let l=s.getAttribute("value");r.getAttribute("value")!==l&&!o.includes("value")&&(r.value=l??"",c=!0),c=a(r,s,"checked")||c,a(r,s,"disabled")}else if(r instanceof HTMLTextAreaElement&&s instanceof HTMLTextAreaElement){let l=s.value;r.defaultValue!==l&&(r.value=l,c=!0)}else r instanceof HTMLOptionElement&&s instanceof HTMLOptionElement&&(c=a(r,s,"selected")||c);for(let{name:l,value:u}of s.attributes)r.getAttribute(l)!==u&&!o.includes(l)&&r.setAttribute(l,u);for(let{name:l}of Array.from(r.attributes))!s.hasAttribute(l)&&!o.includes(l)&&r.removeAttribute(l);c&&(r instanceof HTMLOptionElement?r.closest("select"):r)?.dispatchEvent(new Event(te,{bubbles:!0})),i&&!r.hasAttribute("data-scope-children")&&r.setAttribute("data-scope-children",""),r instanceof HTMLTemplateElement&&s instanceof HTMLTemplateElement?r.innerHTML=s.innerHTML:r.isEqualNode(s)||rn(r,s),i&&r.dispatchEvent(new CustomEvent(Ke,{bubbles:!1}))}return(n===8||n===3)&&e.nodeValue!==t.nodeValue&&(e.nodeValue=t.nodeValue),e},tn=(e,t)=>{for(let n of t)if(ue.has(n.id)){let r=n;for(;r&&r!==e;){let s=I.get(r);s||(s=new Set,I.set(r,s)),s.add(n.id),r=r.parentElement}}};Ee({name:"datastar-patch-signals",apply({error:e},{signals:t,onlyIfMissing:n}){if(typeof t!="string")throw e("PatchSignalsExpectedSignals");let r=typeof n=="string"&&n.trim()==="true";D(ae(t),{ifMissing:r})}});export{V as action,kt as actions,g as attribute,N as beginBatch,_e as computed,R as effect,P as endBatch,$ as filtered,W as getPath,D as mergePatch,S as mergePaths,se as root,me as signal,_ as startPeeking,k as stopPeeking,Ee as watcher}; +//# sourceMappingURL=datastar.js.map diff --git a/rebar.config b/rebar.config index a60e960..fb88e18 100644 --- a/rebar.config +++ b/rebar.config @@ -1,9 +1,14 @@ {erl_opts, [debug_info]}. {deps, [ - nova, - {arizona_core, {git, "https://github.com/novaframework/arizona_core.git", {branch, "main"}}}, - {arizona_nova, {git, "https://github.com/novaframework/arizona_nova.git", {branch, "main"}}} + %% Nova + datastar are pre-release; pin immutable SHAs (the streaming + %% return-handler pattern depends on this Nova revision - see nova#387). + {nova, + {git, "https://github.com/novaframework/nova.git", + {ref, "9ee7e21ef5f82c9c8b71b0ff6bf0ace08d8b72a3"}}}, + {datastar, + {git, "https://github.com/Taure/datastar.git", + {ref, "fcc11f399294b305620a7c99ed633b898ce3889b"}}} ]}. {project_plugins, [ @@ -22,7 +27,10 @@ {xref_checks, [ undefined_function_calls, - undefined_functions + undefined_functions, + locals_not_used, + deprecated_function_calls, + deprecated_functions ]}. {xref_ignores, [ @@ -32,18 +40,10 @@ {kura_schema, indexes, 1} ]}. -{dialyzer, [ - {plt_apps, all_deps}, - {exclude_mods, [ - nova_liveboard_layout, - nova_liveboard_system_view, - nova_liveboard_processes_view, - nova_liveboard_ets_view, - nova_liveboard_apps_view, - nova_liveboard_ports_view, - nova_liveboard_sup_view, - nova_liveboard_metrics_view, - nova_liveboard_database_view, - nova_liveboard_schemas_view - ]} +{dialyzer, [{plt_apps, all_deps}]}. + +{overrides, [ + %% redbug (pulled transitively via nova) uses the deprecated `catch`, + %% which OTP 29 promotes to an error under warnings_as_errors. + {override, redbug, [{erl_opts, [debug_info, nowarn_deprecated_catch]}]} ]}. diff --git a/rebar.lock b/rebar.lock index e17baff..b661b42 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,50 +1,34 @@ {"1.2.0", -[{<<"arizona_core">>, - {git,"https://github.com/novaframework/arizona_core.git", - {ref,"b0dcb87f9e3e64c7ddd51a5e4f6ca24c880cf0c4"}}, +[{<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.13.0">>},1}, + {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.16.1">>},2}, + {<<"datastar">>, + {git,"https://github.com/Taure/datastar.git", + {ref,"fcc11f399294b305620a7c99ed633b898ce3889b"}}, 0}, - {<<"arizona_nova">>, - {git,"https://github.com/novaframework/arizona_nova.git", - {ref,"44e58e46e14b8e1ad9a2e48b637154b0a630ad6f"}}, - 0}, - {<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.13.0">>},1}, - {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.16.0">>},2}, - {<<"eqwalizer_support">>, - {git_subdir,"https://github.com/whatsapp/eqwalizer.git", - {ref,"80d961978515425b7c897f32da31941cb180d2eb"}, - "eqwalizer_support"}, - 1}, {<<"erlydtl">>,{pkg,<<"erlydtl">>,<<"0.14.0">>},1}, {<<"jhn_stdlib">>,{pkg,<<"jhn_stdlib">>,<<"5.4.0">>},1}, - {<<"markdown">>, - {git_subdir,"https://github.com/arizona-framework/erlang-markdown.git", - {ref,"c59493c94aa8dbc105a739b8d1e7a9ccf5c2b338"}, - "apps/markdown"}, - 1}, - {<<"nova">>,{pkg,<<"nova">>,<<"0.13.7">>},0}, + {<<"nova">>, + {git,"https://github.com/novaframework/nova.git", + {ref,"7a474e312344d507db5f216569d4c31b98727d77"}}, + 0}, {<<"ranch">>,{pkg,<<"ranch">>,<<"2.2.0">>},2}, - {<<"redbug">>,{pkg,<<"redbug">>,<<"2.1.0">>},2}, {<<"routing_tree">>,{pkg,<<"routing_tree">>,<<"1.0.11">>},1}, {<<"thoas">>,{pkg,<<"thoas">>,<<"1.2.1">>},1}]}. [ {pkg_hash,[ {<<"cowboy">>, <<"09D770DD5F6A22CC60C071F432CD7CB87776164527F205C5A6B0F24FF6B38990">>}, - {<<"cowlib">>, <<"54592074EBBBB92EE4746C8A8846E5605052F29309D3A873468D76CDF932076F">>}, + {<<"cowlib">>, <<"318D385D55F657E9A5005838C4E426E13DCD724A691438384B6165A69687E531">>}, {<<"erlydtl">>, <<"964B2DC84F8C17ACFAA69C59BA129EF26AC45D2BA898C3C6AD9B5BDC8BA13CED">>}, {<<"jhn_stdlib">>, <<"FAC6F19B35351278F1CB156E23A5B2A6047A9DD5AB1FD9E1189A7918006DF7ED">>}, - {<<"nova">>, <<"C37A2161EA1EE643635739282D1DF9880D20D9521B6AD3ABE6A71C6A6867C842">>}, {<<"ranch">>, <<"25528F82BC8D7C6152C57666CA99EC716510FE0925CB188172F41CE93117B1B0">>}, - {<<"redbug">>, <<"30477A303BF6A62EF02D7C7AC53A6325104DBB7E791D52FE9E7F8B92488B07EF">>}, {<<"routing_tree">>, <<"72ACEF2095F0EC804F7AFD07EF781DDE5009425A1CA0A28F0706B1DB334A4812">>}, {<<"thoas">>, <<"19A25F31177A17E74004D4840F66D791D4298C5738790FA2CC73731EB911F195">>}]}, {pkg_hash_ext,[ {<<"cowboy">>, <<"E724D3A70995025D654C1992C7B11DBFEA95205C047D86FF9BF1CDA92DDC5614">>}, - {<<"cowlib">>, <<"7F478D80D66B747344F0EA7708C187645CFCC08B11AA424632F78E25BF05DB51">>}, + {<<"cowlib">>, <<"58F1E425A9E04176F1D30E20116F57C4E90EF0E187552E9741C465BDF4044F70">>}, {<<"erlydtl">>, <<"D80EC044CD8F58809C19D29AC5605BE09E955040911B644505E31E9DD8143431">>}, {<<"jhn_stdlib">>, <<"7EABD1B01D2DEFF495BF7C5CA1DBA4D3FA0B84DC3AF03CA85F31D52EBB03C6FC">>}, - {<<"nova">>, <<"22F4C3FFDC08DB8568F3DA198647823D63123520F69C7D718A8DB468F4C7A1EA">>}, {<<"ranch">>, <<"FA0B99A1780C80218A4197A59EA8D3BDAE32FBFF7E88527D7D8A4787EFF4F8E7">>}, - {<<"redbug">>, <<"55D6D59697481CA4CC5AD54749AA6D78299AA8A8096027E7AE1F59DB9DC94C78">>}, {<<"routing_tree">>, <<"85982C7AC502892C5179CD2A591331003BACD2D2A71723640BA7D23F45408E6E">>}, {<<"thoas">>, <<"E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A">>}]} ]. diff --git a/src/nova_liveboard.app.src b/src/nova_liveboard.app.src index 6a1f251..34de0b7 100644 --- a/src/nova_liveboard.app.src +++ b/src/nova_liveboard.app.src @@ -1,18 +1,20 @@ {application, nova_liveboard, [ - {description, "Real-time BEAM VM dashboard for Nova"}, + {description, "Real-time BEAM VM dashboard for Nova (Nova + Datastar)"}, {vsn, git}, - {registered, []}, + {registered, [nova_liveboard_tracer]}, {mod, {nova_liveboard_app, []}}, {applications, [ kernel, stdlib, nova, - arizona_core, - arizona_nova + datastar ]}, {env, [ - {nova_apps, [arizona_nova]} + {prefix, "/liveboard"}, + {refresh_ms, 2000}, + {request_buffer, 200} ]}, {modules, []}, - {licenses, ["Apache-2.0"]} + {licenses, ["Apache-2.0"]}, + {links, [{"GitHub", "https://github.com/novaframework/nova_liveboard"}]} ]}. diff --git a/src/nova_liveboard.erl b/src/nova_liveboard.erl index f8dd5fd..8857956 100644 --- a/src/nova_liveboard.erl +++ b/src/nova_liveboard.erl @@ -1,10 +1,30 @@ -module(nova_liveboard). +-moduledoc """ +Configuration helpers for the liveboard. --export([prefix/0]). +All settings live under the `nova_liveboard` application env: +- `prefix` (default `"/liveboard"`) - the path the dashboard mounts on. +- `refresh_ms` (default `2000`) - how often the polled SSE streams repaint. +- `request_buffer` (default `200`) - how many recent requests the tracer keeps. +""". + +-export([prefix/0, refresh_ms/0, request_buffer/0]). + +-doc "The mount path, always as a binary with a leading slash.". -spec prefix() -> binary(). prefix() -> case application:get_env(nova_liveboard, prefix, ~"/liveboard") of - Prefix when is_binary(Prefix) -> Prefix; - Prefix when is_list(Prefix) -> list_to_binary(Prefix) + P when is_binary(P) -> P; + P when is_list(P) -> list_to_binary(P) end. + +-doc "Repaint interval for the polled (non-request) streams, in milliseconds.". +-spec refresh_ms() -> pos_integer(). +refresh_ms() -> + application:get_env(nova_liveboard, refresh_ms, 2000). + +-doc "How many completed requests the tracer ring buffer retains.". +-spec request_buffer() -> pos_integer(). +request_buffer() -> + application:get_env(nova_liveboard, request_buffer, 200). diff --git a/src/nova_liveboard_action_controller.erl b/src/nova_liveboard_action_controller.erl new file mode 100644 index 0000000..2c82366 --- /dev/null +++ b/src/nova_liveboard_action_controller.erl @@ -0,0 +1,43 @@ +-module(nova_liveboard_action_controller). +-moduledoc """ +POST handlers for the Requests page controls (arm / disarm / clear deep trace). + +Each mutates `nova_liveboard_tracer` and returns a one-shot Datastar response: +a normal `{status, 200, ...}` reply whose body is SSE-framed patch events. +Nova's built-in `handle_status` sends it, and Datastar applies the patches to +the clicking client immediately. Other open clients converge via the tracer's +own notifications to their Requests stream. +""". + +-export([trace_start/1, trace_stop/1, clear/1]). + +-define(ARM_COUNT, 10). + +trace_start(_Req) -> + nova_liveboard_tracer:start_trace(?ARM_COUNT), + oneshot([toolbar_patch()]). + +trace_stop(_Req) -> + nova_liveboard_tracer:stop_trace(), + oneshot([toolbar_patch()]). + +clear(_Req) -> + nova_liveboard_tracer:clear(), + oneshot([toolbar_patch(), feed_cleared_patch()]). + +%% --------------------------------------------------------------------------- + +oneshot(Frames) -> + {status, 200, maps:from_list(datastar:sse_headers()), iolist_to_binary(Frames)}. + +toolbar_patch() -> + datastar:patch_elements( + nova_liveboard_html:requests_toolbar_html(nova_liveboard_tracer:trace_state()), + #{selector => ~"#req-toolbar", mode => inner} + ). + +feed_cleared_patch() -> + datastar:patch_elements( + ~"

cleared - waiting for the next request

", + #{selector => ~"#req-feed", mode => inner} + ). diff --git a/src/nova_liveboard_app.erl b/src/nova_liveboard_app.erl index 4ebccd1..409375e 100644 --- a/src/nova_liveboard_app.erl +++ b/src/nova_liveboard_app.erl @@ -1,10 +1,21 @@ -module(nova_liveboard_app). +-moduledoc """ +Application entry point. + +Starts the supervision tree (the request tracer) and registers the +`{stream, ...}` Datastar SSE return-handler with Nova. The request-tracing +plugin is *not* auto-registered: it is a global Nova plugin, so the host wires +it into its own `{nova, [{plugins, ...}]}` config (see the README). The +dashboard's pages work either way; only the Requests feed needs the plugin. +""". +-behaviour(application). -export([start/2, stop/1]). start(_StartType, _StartArgs) -> - arizona_nova:register_views(nova_liveboard, fun nova_liveboard_controller:resolve_view/1), - {ok, self()}. + {ok, Sup} = nova_liveboard_sup:start_link(), + ok = nova_liveboard_sse:register(), + {ok, Sup}. stop(_State) -> ok. diff --git a/src/nova_liveboard_apps_view.erl b/src/nova_liveboard_apps_view.erl deleted file mode 100644 index 988594e..0000000 --- a/src/nova_liveboard_apps_view.erl +++ /dev/null @@ -1,64 +0,0 @@ --module(nova_liveboard_apps_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_info/2]). - -mount(_Arg, _Req) -> - case arizona_live:is_connected(self()) of - true -> erlang:send_after(5000, self(), refresh); - false -> ok - end, - Apps = nova_liveboard_data:running_applications(), - Prefix = nova_liveboard:prefix(), - Bindings = #{ - id => ~"apps_view", - applications => Apps - }, - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"applications", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - Apps = arizona_template:get_binding(applications, Bindings), - arizona_template:from_html( - ~"""" -
-

{integer_to_binary(length(Apps))} running applications

-
- - - - - - - - - - {arizona_template:render_list(fun(App) -> - arizona_template:from_html(~""" - - - - - - """) - end, Apps)} - -
ApplicationVersionDescription
{maps:get(name, App)}{maps:get(version, App)}{maps:get(description, App)}
-
-
- """" - ). - -handle_info(refresh, View) -> - erlang:send_after(5000, self(), refresh), - Apps = nova_liveboard_data:running_applications(), - State = arizona_view:get_state(View), - UpdatedState = arizona_stateful:put_binding(applications, Apps, State), - {[], arizona_view:update_state(UpdatedState, View)}. diff --git a/src/nova_liveboard_controller.erl b/src/nova_liveboard_controller.erl deleted file mode 100644 index 6ff12e2..0000000 --- a/src/nova_liveboard_controller.erl +++ /dev/null @@ -1,58 +0,0 @@ --module(nova_liveboard_controller). - --export([index/1, resolve_view/1]). - --spec index(Req :: map()) -> {status, integer(), map(), iodata()}. -index(CowboyReq) -> - Path = cowboy_req:path(CowboyReq), - {ViewModule, MountArg} = resolve_view_module(Path), - ArizonaReq = arizona_cowboy_request:new(CowboyReq), - try - View = arizona_view:call_mount_callback(ViewModule, MountArg, ArizonaReq), - {Html, _RenderView} = arizona_renderer:render_layout(View), - {status, 200, #{<<"content-type">> => <<"text/html; charset=utf-8">>}, Html} - catch - Error:Reason:Stacktrace -> - logger:error(~"Liveboard render error: ~p:~p~n~p", [Error, Reason, Stacktrace]), - {status, 500, #{<<"content-type">> => <<"text/html">>}, <<"Internal Server Error">>} - end. - --spec resolve_view(map()) -> {view, module(), term(), list()}. -resolve_view(#{path := Path}) -> - {ViewModule, MountArg} = resolve_view_module(Path), - {view, ViewModule, MountArg, []}. - -resolve_view_module(Path) -> - case page_from_path(Path) of - <<"processes">> -> {nova_liveboard_processes_view, undefined}; - <<"ets">> -> {nova_liveboard_ets_view, undefined}; - <<"applications">> -> {nova_liveboard_apps_view, undefined}; - <<"ports">> -> {nova_liveboard_ports_view, undefined}; - <<"supervisors">> -> {nova_liveboard_sup_view, undefined}; - <<"metrics">> -> {nova_liveboard_metrics_view, undefined}; - <<"database">> -> {nova_liveboard_database_view, undefined}; - <<"schemas">> -> {nova_liveboard_schemas_view, undefined}; - _ -> {nova_liveboard_system_view, undefined} - end. - -page_from_path(Path) -> - Pages = [ - <<"processes">>, - <<"ets">>, - <<"applications">>, - <<"ports">>, - <<"supervisors">>, - <<"metrics">>, - <<"database">>, - <<"schemas">> - ], - case binary:split(Path, <<"/">>, [global, trim_all]) of - [] -> - <<"system">>; - Parts -> - Last = lists:last(Parts), - case lists:member(Last, Pages) of - true -> Last; - false -> <<"system">> - end - end. diff --git a/src/nova_liveboard_data.erl b/src/nova_liveboard_data.erl index e283fa9..689a96f 100644 --- a/src/nova_liveboard_data.erl +++ b/src/nova_liveboard_data.erl @@ -22,6 +22,22 @@ kura_schemas/1 ]). +%% These functions either call Kura's optional, runtime-detected API +%% (kura_repo/kura_schema/kura_migrator are not dependencies) or work with ETS +%% opaque tids and the undocumented scheduler_wall_time_all tuple; both are +%% unresolvable for dialyzer by design, so the warnings are suppressed at source. +-dialyzer( + {nowarn_function, [ + scheduler_info/0, + kura_repo_info/1, + kura_migration_status/1, + schema_info/1, + format_ets_id/1, + scheduler_wall_time/0, + pool_stats_from_pid/2 + ]} +). + -spec system_info() -> map(). system_info() -> {Total, Allocated, _Worst} = memsup_or_vm_memory(), @@ -35,6 +51,7 @@ system_info() -> port_limit => erlang:system_info(port_limit), atom_count => erlang:system_info(atom_count), atom_limit => erlang:system_info(atom_limit), + run_queue => erlang:statistics(run_queue), ets_count => length(ets:all()), scheduler_count => erlang:system_info(schedulers), scheduler_online => erlang:system_info(schedulers_online), diff --git a/src/nova_liveboard_database_view.erl b/src/nova_liveboard_database_view.erl deleted file mode 100644 index 967b625..0000000 --- a/src/nova_liveboard_database_view.erl +++ /dev/null @@ -1,145 +0,0 @@ --module(nova_liveboard_database_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_info/2]). - -mount(_Arg, _Req) -> - case arizona_live:is_connected(self()) of - true -> erlang:send_after(5000, self(), refresh); - false -> ok - end, - Repos = nova_liveboard_data:kura_repos(), - RepoData = [build_repo_data(R) || R <- Repos], - Prefix = nova_liveboard:prefix(), - Bindings = #{ - id => ~"database_view", - repos => RepoData - }, - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"database", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - Repos = arizona_template:get_binding(repos, Bindings), - arizona_template:from_html( - ~"""" -
-

Auto-refreshes every 5s

- {arizona_template:render_list(fun(Repo) -> - arizona_template:from_html(~""" -
-
{maps:get(module, Repo)}
- -
-
-
Database
-
{maps:get(database, Repo)}
-
{maps:get(host_display, Repo)}
-
-
-
Pool Status
-
{maps:get(pool_status, Repo)}
-
size: {maps:get(pool_size_display, Repo)}
-
-
-
Available
-
{maps:get(available_display, Repo)}
-
-
-
-
-
-
Checked Out
-
{maps:get(checked_out_display, Repo)}
-
-
-
-
-
- -
Migrations ({maps:get(applied_display, Repo)} applied, {maps:get(pending_display, Repo)} pending)
- {maps:get(migration_html, Repo)} -
- """) - end, Repos)} -
- """" - ). - -handle_info(refresh, View) -> - erlang:send_after(5000, self(), refresh), - Repos = nova_liveboard_data:kura_repos(), - RepoData = [build_repo_data(R) || R <- Repos], - State = arizona_view:get_state(View), - UpdatedState = arizona_stateful:put_binding(repos, RepoData, State), - {[], arizona_view:update_state(UpdatedState, View)}. - -%% Internal - -build_repo_data(RepoMod) -> - Info = nova_liveboard_data:kura_repo_info(RepoMod), - Pool = maps:get(pool, Info), - PoolStats = nova_liveboard_data:kura_pool_stats(Pool), - Migrations = nova_liveboard_data:kura_migration_status(RepoMod), - PoolSize = maps:get(pool_size, Info), - Available = maps:get(available, PoolStats), - CheckedOut = maps:get(checked_out, PoolStats), - PendingCount = length([M || M <- Migrations, maps:get(status, M) =:= ~"pending"]), - AppliedCount = length([M || M <- Migrations, maps:get(status, M) =:= ~"up"]), - #{ - module => maps:get(module, Info), - database => maps:get(database, Info), - host_display => iolist_to_binary([ - maps:get(hostname, Info), ~":", integer_to_binary(maps:get(port, Info)) - ]), - pool_status => maps:get(status, PoolStats), - status_class => pool_status_class(maps:get(status, PoolStats)), - pool_size_display => integer_to_binary(PoolSize), - available_display => integer_to_binary(Available), - available_pct => pool_pct(Available, PoolSize), - checked_out_display => integer_to_binary(CheckedOut), - checked_out_pct => pool_pct(CheckedOut, PoolSize), - applied_display => integer_to_binary(AppliedCount), - pending_display => integer_to_binary(PendingCount), - migration_html => migration_table_html(Migrations) - }. - -migration_table_html([]) -> - ~"

No migrations found

"; -migration_table_html(Migrations) -> - Header = - <<"", "", - "">>, - Rows = [migration_row(M) || M <- Migrations], - Footer = ~"
VersionModuleStatus
", - iolist_to_binary([Header, Rows, Footer]). - -migration_row(Mig) -> - Badge = - case maps:get(status, Mig) of - ~"up" -> ~"up"; - ~"pending" -> ~"pending"; - S -> S - end, - iolist_to_binary([ - ~"", - maps:get(version, Mig), - ~"", - maps:get(module, Mig), - ~"", - Badge, - ~"" - ]). - -pool_status_class(~"ready") -> ~"text-green"; -pool_status_class(~"busy") -> ~"text-amber"; -pool_status_class(_) -> ~"text-dim". - -pool_pct(_, 0) -> ~"0"; -pool_pct(Val, Total) -> integer_to_binary(min(100, (Val * 100) div Total)). diff --git a/src/nova_liveboard_ets_view.erl b/src/nova_liveboard_ets_view.erl deleted file mode 100644 index 9907b8d..0000000 --- a/src/nova_liveboard_ets_view.erl +++ /dev/null @@ -1,76 +0,0 @@ --module(nova_liveboard_ets_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_info/2]). - -mount(_Arg, _Req) -> - case arizona_live:is_connected(self()) of - true -> erlang:send_after(3000, self(), refresh); - false -> ok - end, - Tables = nova_liveboard_data:ets_tables(), - Sorted = lists:sort( - fun(A, B) -> maps:get(memory_bytes, A) >= maps:get(memory_bytes, B) end, Tables - ), - Prefix = nova_liveboard:prefix(), - Bindings = #{ - id => ~"ets_view", - tables => Sorted - }, - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"ets", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - Tables = arizona_template:get_binding(tables, Bindings), - arizona_template:from_html( - ~"""" -
-

{integer_to_binary(length(Tables))} tables · Auto-refreshes every 3s

-
- - - - - - - - - - - - - {arizona_template:render_list(fun(Table) -> - arizona_template:from_html(~""" - - - - - - - - - """) - end, Tables)} - -
NameTypeProtectionSizeMemoryOwner
{maps:get(name, Table)}{maps:get(type, Table)}{maps:get(protection, Table)}{nova_liveboard_data:format_number(maps:get(size, Table))}{nova_liveboard_data:format_bytes(maps:get(memory_bytes, Table))}{maps:get(owner, Table)}
-
-
- """" - ). - -handle_info(refresh, View) -> - erlang:send_after(3000, self(), refresh), - Tables = nova_liveboard_data:ets_tables(), - Sorted = lists:sort( - fun(A, B) -> maps:get(memory_bytes, A) >= maps:get(memory_bytes, B) end, Tables - ), - State = arizona_view:get_state(View), - UpdatedState = arizona_stateful:put_binding(tables, Sorted, State), - {[], arizona_view:update_state(UpdatedState, View)}. diff --git a/src/nova_liveboard_html.erl b/src/nova_liveboard_html.erl new file mode 100644 index 0000000..5e85de7 --- /dev/null +++ b/src/nova_liveboard_html.erl @@ -0,0 +1,876 @@ +-module(nova_liveboard_html). +-moduledoc """ +Pure HTML rendering for the liveboard - "BEAM mission control". + +Every function here takes already-gathered data (from `nova_liveboard_data` +and `nova_liveboard_tracer`) and returns `iodata()`. No process state, no +side effects beyond reading the static `nova_liveboard:prefix/0` config, so +the whole module is unit-testable in isolation. + +`page/4` renders the full document shell (vitals deck + instrument nav + +main stage). The per-region functions (`vitals_html/1`, `overview_html/1`, +`processes_html/2`, ...) render the *inner* of a live region; the page +controller uses them for the first paint and `nova_liveboard_sse` re-emits +them as Datastar patches as the VM changes. +""". + +-export([ + page/4, + vitals_html/1, + overview_html/1, + metrics_html/1, + processes_html/2, + ets_html/1, + apps_html/1, + ports_html/1, + sup_tree_html/3, + requests_html/3, + requests_toolbar_html/1, + request_row/1, + request_detail_html/1, + database_html/1, + schemas_html/1, + csp_headers/0, + html_escape/1 +]). + +-define(NAV, [ + {overview, ~"Overview", ~"\x{25C8}"}, + {processes, ~"Processes", ~"\x{2261}"}, + {metrics, ~"Metrics", ~"\x{2248}"}, + {requests, ~"Requests", ~"\x{21AF}"}, + {supervisors, ~"Supervisors", ~"\x{2638}"}, + {applications, ~"Applications", ~"\x{25A4}"}, + {ets, ~"ETS", ~"\x{229E}"}, + {ports, ~"Ports", ~"\x{21C4}"} +]). + +%% --------------------------------------------------------------------------- +%% document shell +%% --------------------------------------------------------------------------- + +-doc """ +Full HTML document: head + vitals deck + nav + the main stage. + +`StreamPath` is the SSE path the main region should subscribe to (so it +repaints live), or `none` for a static page (e.g. a request detail) that must +not be overwritten by a stream. +""". +-spec page(atom(), binary() | none, iodata(), iodata()) -> iodata(). +page(Active, StreamPath, VitalsInner, MainInner) -> + P = nova_liveboard:prefix(), + [ + ~"", + ~"", + ~"nova liveboard \x{00B7} ", + page_title(Active), + ~"", + ~"", + ~"
", + header_html(P, VitalsInner), + ~"
", + nav_html(P, Active), + ~"
", + stage_inner(StreamPath, MainInner), + ~"
" + ]. + +stage_inner(none, MainInner) -> + [~"
", MainInner, ~"
"]; +stage_inner(StreamPath, MainInner) -> + [ + ~"
", + MainInner, + ~"
" + ]. + +header_html(P, VitalsInner) -> + [ + ~"
\x{2726}", + ~"novaliveboard", + ~"live
", + %% data-init opens the always-on vitals stream; #vitals is patched inner. + ~"
", + VitalsInner, + ~"
" + ]. + +nav_html(P, Active) -> + Items = [nav_item(P, Active, Page, Label, Glyph) || {Page, Label, Glyph} <- ?NAV], + Kura = + case nova_liveboard_data:kura_available() of + true -> + [ + ~"
data
", + nav_item(P, Active, database, ~"Database", ~"\x{2317}"), + nav_item(P, Active, schemas, ~"Schemas", ~"\x{229F}") + ]; + false -> + [] + end, + [~""]. + +nav_item(P, Active, Page, Label, Glyph) -> + Cls = + case Page =:= Active of + true -> ~"item active"; + false -> ~"item" + end, + [ + ~"", + Glyph, + ~"", + Label, + ~"" + ]. + +nav_path(overview) -> ~""; +nav_path(Page) -> atom_to_binary(Page). + +page_title(overview) -> ~"overview"; +page_title(Page) -> atom_to_binary(Page). + +%% --------------------------------------------------------------------------- +%% vitals deck (always-on header strip) +%% --------------------------------------------------------------------------- + +-doc "The live header gauges: capacity vs limits + memory + uptime.". +-spec vitals_html(map()) -> iodata(). +vitals_html(Sys) -> + #{ + process_count := Procs, + process_limit := ProcLimit, + port_count := Ports, + port_limit := PortLimit, + atom_count := Atoms, + atom_limit := AtomLimit, + run_queue := RunQ, + uptime := Uptime, + memory := Mem + } = Sys, + Total = maps:get(total, Mem), + [ + vital_gauge(~"processes", Procs, ProcLimit), + vital_gauge(~"ports", Ports, PortLimit), + vital_gauge(~"atoms", Atoms, AtomLimit), + vital_stat(~"run queue", integer_to_binary(RunQ)), + vital_stat(~"memory", nova_liveboard_data:format_bytes(Total)), + vital_stat(~"uptime", nova_liveboard_data:format_uptime(Uptime)) + ]. + +vital_gauge(Label, Used, Limit) -> + Pct = pct(Used, Limit), + [ + ~"
", + Label, + ~"", + nova_liveboard_data:format_number(Used), + ~"/", + nova_liveboard_data:format_number(Limit), + ~"
", + bar(Pct, util_class(Pct)), + ~"
" + ]. + +vital_stat(Label, Value) -> + [ + ~"
", + Label, + ~"
", + Value, + ~"
" + ]. + +%% --------------------------------------------------------------------------- +%% overview +%% --------------------------------------------------------------------------- + +-doc "System identity + memory breakdown + capacity panels.". +-spec overview_html(map()) -> iodata(). +overview_html(Sys) -> + #{ + otp_release := Otp, + erts_version := Erts, + system_architecture := Arch, + scheduler_count := Scheds, + scheduler_online := SchedsOn, + ets_count := EtsCount, + memory := Mem + } = Sys, + [ + ~"
", + panel( + ~"node", + [ + kv(~"otp release", Otp), + kv(~"erts", Erts), + kv(~"architecture", Arch), + kv(~"schedulers", [ + integer_to_binary(SchedsOn), ~" / ", integer_to_binary(Scheds) + ]), + kv(~"ets tables", integer_to_binary(EtsCount)) + ] + ), + panel(~"memory", memory_bars(Mem)), + ~"
" + ]. + +memory_bars(Mem) -> + Total = maps:get(total, Mem), + Rows = [ + {~"processes", maps:get(processes, Mem)}, + {~"binary", maps:get(binary, Mem)}, + {~"ets", maps:get(ets, Mem)}, + {~"code", maps:get(code, Mem)}, + {~"atom", maps:get(atom, Mem)}, + {~"system", maps:get(system, Mem)} + ], + [ + [ + ~"
", + Label, + ~"", + nova_liveboard_data:format_bytes(Bytes), + ~"
", + bar(pct(Bytes, Total), ~"cool"), + ~"
" + ] + || {Label, Bytes} <- Rows + ]. + +%% --------------------------------------------------------------------------- +%% metrics (sparklines + scheduler bars) +%% --------------------------------------------------------------------------- + +-doc "Live sparklines for memory/IO/run-queue + scheduler utilisation bars.". +-spec metrics_html(map()) -> iodata(). +metrics_html(M) -> + Spark = fun(Key, Label, Fmt) -> + Vals = queue:to_list(maps:get(Key, M)), + spark_card(Label, Fmt(last_val(Vals)), Vals) + end, + Bytes = fun nova_liveboard_data:format_bytes/1, + Int = fun integer_to_binary/1, + [ + ~"
", + Spark(total_memory, ~"total memory", Bytes), + Spark(process_memory, ~"process memory", Bytes), + Spark(binary_memory, ~"binary memory", Bytes), + Spark(process_count, ~"processes", Int), + Spark(run_queue, ~"run queue", Int), + Spark(io_input, ~"io in / tick", Bytes), + Spark(io_output, ~"io out / tick", Bytes), + ~"
", + panel(~"scheduler utilisation", sched_bars(maps:get(scheduler_util, M, []))) + ]. + +spark_card(Label, Value, Vals) -> + Pts = nova_liveboard_data:sparkline_points(Vals, 240, 48), + [ + ~"
", + Label, + ~"
", + Value, + ~"
", + ~"
" + ]. + +sched_bars([]) -> + empty(~"collecting scheduler samples..."); +sched_bars(Utils) -> + [ + begin + Pct = min(100, round(maps:get(util, U))), + PctB = integer_to_binary(Pct), + [ + ~"
S", + integer_to_binary(maps:get(id, U)), + ~"", + bar(Pct, util_class(Pct)), + ~"", + PctB, + ~"%
" + ] + end + || U <- Utils + ]. + +%% --------------------------------------------------------------------------- +%% processes +%% --------------------------------------------------------------------------- + +-doc "Top processes table, sorted by `SortBy`, with sort controls.". +-spec processes_html([map()], atom()) -> iodata(). +processes_html(Procs, SortBy) -> + [ + ~"
sort by", + sort_btn(SortBy, memory, ~"memory"), + sort_btn(SortBy, reductions, ~"reductions"), + sort_btn(SortBy, message_queue_len, ~"msg queue"), + ~"
", + table( + [~"pid", ~"name", ~"memory", ~"reductions", ~"msgq", ~"current"], + [ + [ + cell_mono(maps:get(pid, Pr)), + cell(maps:get(name, Pr)), + cell_num(nova_liveboard_data:format_bytes(maps:get(memory, Pr))), + cell_num(nova_liveboard_data:format_number(maps:get(reductions, Pr))), + cell_num(integer_to_binary(maps:get(message_queue_len, Pr))), + cell_mono(maps:get(current_function, Pr)) + ] + || Pr <- Procs + ] + ) + ]. + +sort_btn(Active, Key, Label) -> + P = nova_liveboard:prefix(), + Cls = + case Active =:= Key of + true -> ~"chip active"; + false -> ~"chip" + end, + %% A plain link so the single page stream reopens with the new sort, rather + %% than stacking a second EventSource over the running one. + [ + ~"", + Label, + ~"" + ]. + +%% --------------------------------------------------------------------------- +%% ets / applications / ports +%% --------------------------------------------------------------------------- + +-doc "ETS tables table.". +-spec ets_html([map()]) -> iodata(). +ets_html(Tables) -> + table( + [~"name", ~"id", ~"type", ~"protection", ~"size", ~"memory", ~"owner"], + [ + [ + cell(maps:get(name, T)), + cell_mono(maps:get(id, T)), + cell(maps:get(type, T)), + cell(maps:get(protection, T)), + cell_num(nova_liveboard_data:format_number(maps:get(size, T))), + cell_num(nova_liveboard_data:format_bytes(maps:get(memory_bytes, T))), + cell_mono(maps:get(owner, T)) + ] + || T <- Tables + ] + ). + +-doc "Running applications table.". +-spec apps_html([map()]) -> iodata(). +apps_html(Apps) -> + table( + [~"application", ~"version", ~"description"], + [ + [ + cell_strong(maps:get(name, A)), + cell_mono(maps:get(version, A)), + cell(maps:get(description, A)) + ] + || A <- Apps + ] + ). + +-doc "Open ports table.". +-spec ports_html([map()]) -> iodata(). +ports_html(Ports) -> + table( + [~"id", ~"name", ~"connected", ~"input", ~"output"], + [ + [ + cell_mono(maps:get(id, Po)), + cell(maps:get(name, Po)), + cell_mono(maps:get(connected, Po)), + cell_num(nova_liveboard_data:format_bytes(maps:get(input, Po))), + cell_num(nova_liveboard_data:format_bytes(maps:get(output, Po))) + ] + || Po <- Ports + ] + ). + +%% --------------------------------------------------------------------------- +%% supervision tree +%% --------------------------------------------------------------------------- + +-doc "The supervision tree for `Active` (static process structure) + app chooser.". +-spec sup_tree_html([atom()], atom(), {ok, list()} | {error, term()}) -> iodata(). +sup_tree_html(Apps, Active, Result) -> + [ + ~"
application", + [app_chip(A, Active) || A <- lists:sort(Apps)], + ~"
", + sup_body(Active, Result) + ]. + +app_chip(App, Active) -> + P = nova_liveboard:prefix(), + Name = atom_to_binary(App), + Cls = + case App =:= Active of + true -> ~"chip active"; + false -> ~"chip" + end, + [ + ~"", + html_escape(Name), + ~"" + ]. + +sup_body(_Active, {ok, Tree}) -> + panel(~"supervision tree", [~"
", tree_nodes(Tree, 0), ~"
"]); +sup_body(Active, {error, Reason}) -> + empty([ + ~"no supervisor found for ", + html_escape(atom_to_binary(Active)), + ~" (", + html_escape(to_bin(Reason)), + ~")" + ]). + +tree_nodes(Nodes, Depth) -> + [tree_node(N, Depth) || N <- Nodes]. + +tree_node(Node, Depth) -> + #{type := Type, name := Name, pid := Pid, status := Status} = Node, + Children = maps:get(children, Node, []), + Mem = maps:get(memory, Node, 0), + [ + ~"
", + kind_glyph(Type), + ~"", + html_escape(Name), + ~"", + html_escape(Pid), + ~"", + nova_liveboard_data:format_bytes(Mem), + ~"
", + tree_nodes(Children, Depth + 1) + ]. + +kind_glyph(supervisor) -> ~"\x{2638}"; +kind_glyph(_) -> ~"\x{25CF}". + +kind_class(supervisor) -> ~"sup"; +kind_class(_) -> ~"worker". + +%% --------------------------------------------------------------------------- +%% requests (the live request tracer feed) +%% --------------------------------------------------------------------------- + +-doc """ +The live request feed. `Enabled` reflects whether the tracing plugin is +registered; when it is not, a setup hint is shown instead of an empty feed. +`Trace` is the deep-trace arming state from `nova_liveboard_tracer`. +""". +-spec requests_html([map()], map(), boolean()) -> iodata(). +requests_html(_Requests, _Trace, false) -> + setup_hint(); +requests_html(Requests, Trace, true) -> + [ + ~"
", + requests_toolbar_html(Trace), + ~"
", + case Requests of + [] -> empty(~"no requests captured yet - hit any route on this node"); + _ -> [request_row(R) || R <- Requests] + end, + ~"
" + ]. + +-doc "The arm/disarm/clear toolbar (patched on its own as trace state changes).". +-spec requests_toolbar_html(map()) -> iodata(). +requests_toolbar_html(Trace) -> + P = nova_liveboard:prefix(), + Armed = maps:get(armed, Trace, false), + Remaining = maps:get(remaining, Trace, 0), + {StateCls, StateText} = + case Armed of + true -> + {~"chip active", [ + ~"deep trace armed \x{00B7} ", integer_to_binary(Remaining), ~" left" + ]}; + false -> + {~"chip", ~"deep trace off"} + end, + [ + ~"", + StateText, + ~"", + ~"", + ~"", + ~"" + ]. + +-doc "A single request row (also emitted as a prepended SSE patch on arrival).". +-spec request_row(map()) -> iodata(). +request_row(R) -> + #{ + id := Id, + method := Method, + path := Path, + status := Status, + duration_us := Dur, + reductions := Reds, + handler := Handler, + spawned := Spawned + } = R, + P = nova_liveboard:prefix(), + [ + ~"", + html_escape(Method), + ~"", + html_escape(Path), + ~"", + status_bin(Status), + ~"", + fmt_us(Dur), + ~"", + nova_liveboard_data:format_number(Reds), + ~" reds", + spawn_badge(Spawned), + ~"", + html_escape(Handler), + ~"" + ]. + +spawn_badge(N) when is_integer(N), N > 0 -> + [~"\x{2387} ", integer_to_binary(N)]; +spawn_badge(_) -> + ~"\x{00B7}". + +-doc "Detail page for one captured request: timings + spawned-process tree.". +-spec request_detail_html(map() | undefined) -> iodata(). +request_detail_html(undefined) -> + empty(~"request not found (the buffer may have rolled over)"); +request_detail_html(R) -> + #{ + method := Method, + path := Path, + status := Status, + duration_us := Dur, + reductions := Reds, + mem := Mem, + handler := Handler, + spawned_tree := Tree + } = R, + [ + ~"
", + html_escape(Method), + ~"", + html_escape(Path), + ~"
", + ~"
", + panel(~"timings", [ + kv(~"status", status_bin(Status)), + kv(~"duration", fmt_us(Dur)), + kv(~"reductions", nova_liveboard_data:format_number(Reds)), + kv(~"peak memory", nova_liveboard_data:format_bytes(Mem)), + kv(~"handler", html_escape(Handler)) + ]), + panel( + [~"spawned processes (", integer_to_binary(length(Tree)), ~")"], + spawned_tree_html(Tree) + ), + ~"
" + ]. + +spawned_tree_html([]) -> + empty(~"no processes spawned during this request (or deep trace was off)"); +spawned_tree_html(Tree) -> + [ + ~"
", + [ + [ + ~"
\x{25CF}", + html_escape(maps:get(mfa, S)), + ~"", + html_escape(maps:get(pid, S)), + ~"
" + ] + || S <- Tree + ], + ~"
" + ]. + +setup_hint() -> + [ + ~"

request tracing is off

", + ~"

Add the liveboard tracing plugin to your Nova config to capture every ", + ~"request handled by this node, then watch them stream in here live.

", + ~"
{nova, [\n",
+        ~"  {plugins, [\n",
+        ~"    {pre_request,  nova_liveboard_trace_plugin, #{}},\n",
+        ~"    {post_request, nova_liveboard_trace_plugin, #{}}\n",
+        ~"  ]}\n",
+        ~"]}.
" + ]. + +%% --------------------------------------------------------------------------- +%% kura: database + schemas +%% --------------------------------------------------------------------------- + +-doc "Kura repo pool panels.". +-spec database_html([map()]) -> iodata(). +database_html([]) -> + empty(~"no kura repos detected"); +database_html(Repos) -> + [ + ~"
", + [ + panel(html_escape(maps:get(module, Repo)), [ + kv(~"database", html_escape(maps:get(database, Repo))), + kv(~"host", [ + html_escape(maps:get(hostname, Repo)), ~":", to_bin(maps:get(port, Repo)) + ]), + kv(~"pool size", integer_to_binary(maps:get(pool_size, Repo))), + pool_stat(maps:get(pool, Repo)) + ]) + || Repo <- Repos + ], + ~"
" + ]. + +pool_stat(Stats) -> + Status = maps:get(status, Stats, ~"unknown"), + [ + ~"
pool", + html_escape(Status), + ~" avail ", + integer_to_binary(maps:get(available, Stats, 0)), + ~" \x{00B7} busy ", + integer_to_binary(maps:get(checked_out, Stats, 0)), + ~"
" + ]. + +-doc "Kura schema definitions (fields, associations, indexes).". +-spec schemas_html([map()]) -> iodata(). +schemas_html([]) -> + empty(~"no kura schemas detected"); +schemas_html(Schemas) -> + [ + panel([html_escape(maps:get(module, S)), ~" \x{2192} ", html_escape(maps:get(table, S))], [ + table( + [~"field", ~"type", ~"flags"], + [ + [ + cell_strong(maps:get(name, F)), + cell_mono(maps:get(type, F)), + cell(field_flags(F)) + ] + || F <- maps:get(fields, S) + ] + ) + ]) + || S <- Schemas + ]. + +field_flags(F) -> + PK = + case maps:get(primary_key, F, false) of + true -> [~"pk "]; + false -> [] + end, + V = + case maps:get(virtual, F, false) of + true -> [~"virtual"]; + false -> [] + end, + case [PK, V] of + [[], []] -> ~"\x{00B7}"; + Flags -> Flags + end. + +%% --------------------------------------------------------------------------- +%% shared widgets +%% --------------------------------------------------------------------------- + +panel(Title, Body) -> + [ + ~"

", + Title, + ~"

", + Body, + ~"
" + ]. + +bar(Pct, Cls) -> + PctB = integer_to_binary(min(100, max(0, Pct))), + [ + ~"
" + ]. + +table(Headers, Rows) -> + [ + ~"
", + [[~""] || H <- Headers], + ~"", + case Rows of + [] -> + [ + ~"" + ]; + _ -> + [[~"", Cells, ~""] || Cells <- Rows] + end, + ~"
", H, ~"
nothing here
" + ]. + +cell(V) -> [~"", html_escape(V), ~""]. +cell_strong(V) -> [~"", html_escape(V), ~""]. +cell_mono(V) -> [~"", html_escape(V), ~""]. +cell_num(V) -> [~"", html_escape(V), ~""]. + +kv(K, V) -> + [~"
", K, ~"", V, ~"
"]. + +empty(Msg) -> + [~"

", html_escape(Msg), ~"

"]. + +%% --------------------------------------------------------------------------- +%% classification helpers +%% --------------------------------------------------------------------------- + +util_class(P) when P >= 80 -> ~"hot"; +util_class(P) when P >= 50 -> ~"warm"; +util_class(_) -> ~"cool". + +status_class(~"running") -> ~"ok"; +status_class(~"waiting") -> ~"ok"; +status_class(~"runnable") -> ~"warm"; +status_class(~"restarting") -> ~"bad"; +status_class(~"dead") -> ~"bad"; +status_class(_) -> ~"idle". + +status_code_class(S) when S >= 500 -> ~"s5"; +status_code_class(S) when S >= 400 -> ~"s4"; +status_code_class(S) when S >= 300 -> ~"s3"; +status_code_class(_) -> ~"s2". + +method_class(~"GET") -> ~"get"; +method_class(~"POST") -> ~"post"; +method_class(~"PUT") -> ~"put"; +method_class(~"DELETE") -> ~"delete"; +method_class(_) -> ~"other". + +pool_class(~"ready") -> ~"s2"; +pool_class(~"busy") -> ~"s4"; +pool_class(~"down") -> ~"s5"; +pool_class(_) -> ~"idle". + +status_bin(undefined) -> ~"-"; +status_bin(S) when is_integer(S) -> integer_to_binary(S); +status_bin(S) -> to_bin(S). + +fmt_us(Us) when Us >= 1000000 -> + iolist_to_binary(io_lib:format("~.2f s", [Us / 1000000])); +fmt_us(Us) when Us >= 1000 -> + iolist_to_binary(io_lib:format("~.1f ms", [Us / 1000])); +fmt_us(Us) -> + [integer_to_binary(Us), ~" \x{00B5}s"]. + +pct(_Used, 0) -> 0; +pct(Used, Limit) -> round(Used / Limit * 100). + +last_val([]) -> 0; +last_val(L) -> lists:last(L). + +to_bin(B) when is_binary(B) -> B; +to_bin(A) when is_atom(A) -> atom_to_binary(A); +to_bin(I) when is_integer(I) -> integer_to_binary(I); +to_bin(T) -> iolist_to_binary(io_lib:format("~p", [T])). + +%% --------------------------------------------------------------------------- +%% security + escaping +%% --------------------------------------------------------------------------- + +-doc """ +The dashboard's response headers. Strict CSP: everything is same-origin +(self-hosted datastar.js, fonts, css), so any stray off-origin request fails +loudly. `unsafe-eval` is required only for Datastar's `data-*` expression +evaluation; the privacy-critical directives stay `'self'`. +""". +-spec csp_headers() -> map(). +csp_headers() -> + #{ + ~"content-type" => ~"text/html; charset=utf-8", + ~"content-security-policy" => + ~"default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self'; font-src 'self'; connect-src 'self'; img-src 'self' data:; base-uri 'none'; frame-ancestors 'none'" + }. + +-doc "Escape `& < > \"` for safe interpolation into HTML.". +-spec html_escape(iodata() | atom() | integer()) -> binary(). +html_escape(B) when is_binary(B) -> + B1 = binary:replace(B, ~"&", ~"&", [global]), + B2 = binary:replace(B1, ~"<", ~"<", [global]), + B3 = binary:replace(B2, ~">", ~">", [global]), + binary:replace(B3, ~"\"", ~""", [global]); +html_escape(V) -> + html_escape(to_bin(V)). diff --git a/src/nova_liveboard_layout.erl b/src/nova_liveboard_layout.erl deleted file mode 100644 index c125677..0000000 --- a/src/nova_liveboard_layout.erl +++ /dev/null @@ -1,65 +0,0 @@ --module(nova_liveboard_layout). --compile({parse_transform, arizona_parse_transform}). - --export([render/1]). - --export([kura_nav/2]). - -render(Bindings) -> - arizona_template:from_html( - ~""" - - - - - - Nova Liveboard - - - - - -
- {arizona_template:render_slot(arizona_template:get_binding(main_content, Bindings))} -
- - - """ - ). - -nav_class(Active, Page) when Active =:= Page -> ~"active"; -nav_class(_, _) -> ~"". - -kura_nav(Prefix, Active) -> - case nova_liveboard_data:kura_available() of - true -> - arizona_template:from_html( - ~""" - - >)}">Database - >)}">Schemas - """ - ); - false -> - arizona_template:from_html( - ~""" - - """ - ) - end. diff --git a/src/nova_liveboard_metrics_view.erl b/src/nova_liveboard_metrics_view.erl deleted file mode 100644 index 6fdad96..0000000 --- a/src/nova_liveboard_metrics_view.erl +++ /dev/null @@ -1,163 +0,0 @@ --module(nova_liveboard_metrics_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_info/2]). - -mount(_Arg, _Req) -> - case arizona_live:is_connected(self()) of - true -> erlang:send_after(2000, self(), refresh); - false -> ok - end, - Metrics = nova_liveboard_data:collect_metrics(undefined), - Prefix = nova_liveboard:prefix(), - Bindings = metrics_to_bindings(Metrics), - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"metrics", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - arizona_template:from_html( - ~""""" -
-

Auto-refreshes every 2s

- -
-
-
Total Memory
-
{arizona_template:get_binding(total_mem_val, Bindings)}
- - - -
-
-
Process Memory
-
{arizona_template:get_binding(proc_mem_val, Bindings)}
- - - -
-
-
Binary Memory
-
{arizona_template:get_binding(bin_mem_val, Bindings)}
- - - -
-
-
Process Count
-
{arizona_template:get_binding(proc_count_val, Bindings)}
- - - -
-
-
Run Queue
-
{arizona_template:get_binding(run_queue_val, Bindings)}
- - - -
-
-
IO Input/s
-
{arizona_template:get_binding(io_in_val, Bindings)}
- - - -
-
-
IO Output/s
-
{arizona_template:get_binding(io_out_val, Bindings)}
- - - -
-
- -
-
Scheduler Utilization
-
- {arizona_template:render_list(fun(SchedEntry) -> - Id = maps:get(id, SchedEntry), - Util = maps:get(util, SchedEntry), - Pct = integer_to_binary(min(100, round(Util))), - Color = sched_color(Util), - arizona_template:from_html(~""" -
- S{integer_to_binary(Id)} -
-
-
- {Pct}% -
- """) - end, arizona_template:get_binding(sched_util, Bindings))} -
-
-
- """"" - ). - -handle_info(refresh, View) -> - erlang:send_after(2000, self(), refresh), - State = arizona_view:get_state(View), - OldMetrics = arizona_stateful:get_binding(metrics_state, State), - NewMetrics = nova_liveboard_data:collect_metrics(OldMetrics), - NewB = metrics_to_bindings(NewMetrics), - lists:foldl( - fun({K, V}, S) -> - arizona_stateful:put_binding(K, V, S) - end, - State, - maps:to_list(NewB) - ), - UpdatedState = lists:foldl( - fun({K, V}, S) -> - arizona_stateful:put_binding(K, V, S) - end, - State, - maps:to_list(NewB) - ), - {[], arizona_view:update_state(UpdatedState, View)}. - -%% Internal - -metrics_to_bindings(Metrics) -> - TotalMem = queue:to_list(maps:get(total_memory, Metrics)), - ProcMem = queue:to_list(maps:get(process_memory, Metrics)), - BinMem = queue:to_list(maps:get(binary_memory, Metrics)), - ProcCount = queue:to_list(maps:get(process_count, Metrics)), - RunQueue = queue:to_list(maps:get(run_queue, Metrics)), - IoIn = queue:to_list(maps:get(io_input, Metrics)), - IoOut = queue:to_list(maps:get(io_output, Metrics)), - #{ - id => ~"metrics_view", - metrics_state => Metrics, - total_mem_val => nova_liveboard_data:format_bytes(last_val(TotalMem)), - total_mem_pts => nova_liveboard_data:sparkline_points(TotalMem, 200, 40), - proc_mem_val => nova_liveboard_data:format_bytes(last_val(ProcMem)), - proc_mem_pts => nova_liveboard_data:sparkline_points(ProcMem, 200, 40), - bin_mem_val => nova_liveboard_data:format_bytes(last_val(BinMem)), - bin_mem_pts => nova_liveboard_data:sparkline_points(BinMem, 200, 40), - proc_count_val => integer_to_binary(last_val(ProcCount)), - proc_count_pts => nova_liveboard_data:sparkline_points(ProcCount, 200, 40), - run_queue_val => integer_to_binary(last_val(RunQueue)), - run_queue_pts => nova_liveboard_data:sparkline_points(RunQueue, 200, 40), - io_in_val => nova_liveboard_data:format_bytes(last_val(IoIn)), - io_in_pts => nova_liveboard_data:sparkline_points(IoIn, 200, 40), - io_out_val => nova_liveboard_data:format_bytes(last_val(IoOut)), - io_out_pts => nova_liveboard_data:sparkline_points(IoOut, 200, 40), - sched_util => maps:get(scheduler_util, Metrics) - }. - -last_val([]) -> 0; -last_val(List) -> lists:last(List). - -sched_color(Util) when Util > 80 -> ~"bar-fill-red"; -sched_color(Util) when Util > 50 -> ~"bar-fill-amber"; -sched_color(_) -> ~"bar-fill-blue". diff --git a/src/nova_liveboard_page_controller.erl b/src/nova_liveboard_page_controller.erl new file mode 100644 index 0000000..1d61ca7 --- /dev/null +++ b/src/nova_liveboard_page_controller.erl @@ -0,0 +1,187 @@ +-module(nova_liveboard_page_controller). +-moduledoc """ +Nova controllers for the liveboard pages. + +Each page server-renders the full mission-control shell (`nova_liveboard_html: +page/4`) with a first-paint snapshot, then a `data-init` stream takes over and +repaints the live region. `page_inner/2` is the single place that maps a page +to its data + HTML; both these controllers and `nova_liveboard_sse` call it, so +the first paint and the live patches always agree. +""". + +-export([ + index/1, + processes/1, + ets/1, + applications/1, + ports/1, + supervisors/1, + metrics/1, + requests/1, + request_show/1, + database/1, + schemas/1, + page_inner/2, + page_atom/1, + app_names/0, + default_app/1 +]). + +-define(TOP_N, 50). + +%% --------------------------------------------------------------------------- +%% controllers +%% --------------------------------------------------------------------------- + +index(_Req) -> + full(overview, stream_path(overview), page_inner(overview, undefined)). + +ets(_Req) -> + full(ets, stream_path(ets), page_inner(ets, undefined)). + +applications(_Req) -> + full(applications, stream_path(applications), page_inner(applications, undefined)). + +ports(_Req) -> + full(ports, stream_path(ports), page_inner(ports, undefined)). + +metrics(_Req) -> + full(metrics, stream_path(metrics), page_inner(metrics, undefined)). + +database(_Req) -> + full(database, stream_path(database), page_inner(database, undefined)). + +schemas(_Req) -> + full(schemas, stream_path(schemas), page_inner(schemas, undefined)). + +requests(_Req) -> + full(requests, stream_path(requests), page_inner(requests, undefined)). + +processes(Req) -> + Sort = sort_qs(Req), + Stream = <<(stream_path(processes))/binary, "?sort=", (atom_to_binary(Sort))/binary>>, + full(processes, Stream, page_inner(processes, Sort)). + +supervisors(Req) -> + App = app_qs(Req), + Stream = <<(stream_path(supervisors))/binary, "?app=", (atom_to_binary(App))/binary>>, + full(supervisors, Stream, page_inner(supervisors, App)). + +request_show(Req) -> + Id = maps:get(~"id", maps:get(bindings, Req, #{}), ~""), + %% Static page (stream => none): a stored snapshot must not be overwritten. + full(requests, none, nova_liveboard_html:request_detail_html(nova_liveboard_tracer:get(Id))). + +%% --------------------------------------------------------------------------- +%% shared render dispatch +%% --------------------------------------------------------------------------- + +-doc "Map a page to its freshly-gathered data, rendered to the live region inner.". +-spec page_inner(atom(), term()) -> iodata(). +page_inner(overview, _) -> + nova_liveboard_html:overview_html(nova_liveboard_data:system_info()); +page_inner(processes, Sort) -> + nova_liveboard_html:processes_html(nova_liveboard_data:top_processes(Sort, ?TOP_N), Sort); +page_inner(ets, _) -> + nova_liveboard_html:ets_html(nova_liveboard_data:ets_tables()); +page_inner(applications, _) -> + nova_liveboard_html:apps_html(nova_liveboard_data:running_applications()); +page_inner(ports, _) -> + nova_liveboard_html:ports_html(nova_liveboard_data:port_info()); +page_inner(supervisors, App) -> + nova_liveboard_html:sup_tree_html( + app_names(), App, nova_liveboard_data:supervision_tree(App) + ); +page_inner(metrics, _) -> + nova_liveboard_html:metrics_html(nova_liveboard_data:collect_metrics(undefined)); +page_inner(requests, _) -> + nova_liveboard_html:requests_html( + nova_liveboard_tracer:recent(), nova_liveboard_tracer:trace_state(), tracing_enabled() + ); +page_inner(database, _) -> + nova_liveboard_html:database_html(kura_repos()); +page_inner(schemas, _) -> + nova_liveboard_html:schemas_html(kura_schemas()). + +-doc "Binary page name (from a route binding) to its page atom.". +-spec page_atom(binary()) -> atom(). +page_atom(~"overview") -> overview; +page_atom(~"ets") -> ets; +page_atom(~"applications") -> applications; +page_atom(~"ports") -> ports; +page_atom(~"metrics") -> metrics; +page_atom(~"database") -> database; +page_atom(~"schemas") -> schemas; +page_atom(_) -> overview. + +-spec app_names() -> [atom()]. +app_names() -> + [A || {A, _, _} <- application:which_applications()]. + +-spec default_app([atom()]) -> atom(). +default_app(Names) -> + case application:get_env(nova, bootstrap_application) of + {ok, App} -> App; + _ -> first_user_app(Names) + end. + +%% --------------------------------------------------------------------------- +%% internal +%% --------------------------------------------------------------------------- + +full(Active, Stream, Main) -> + Vitals = nova_liveboard_html:vitals_html(nova_liveboard_data:system_info()), + Body = nova_liveboard_html:page(Active, Stream, Vitals, Main), + {status, 200, nova_liveboard_html:csp_headers(), iolist_to_binary(Body)}. + +stream_path(Page) -> + <<(nova_liveboard:prefix())/binary, "/sse/", (atom_to_binary(Page))/binary>>. + +sort_qs(Req) -> + case proplists:get_value(~"sort", cowboy_req:parse_qs(Req)) of + ~"reductions" -> reductions; + ~"message_queue_len" -> message_queue_len; + _ -> memory + end. + +app_qs(Req) -> + Names = app_names(), + case proplists:get_value(~"app", cowboy_req:parse_qs(Req)) of + undefined -> + default_app(Names); + Bin -> + case lists:search(fun(A) -> atom_to_binary(A) =:= Bin end, Names) of + {value, A} -> A; + false -> default_app(Names) + end + end. + +first_user_app(Names) -> + System = [kernel, stdlib, sasl, nova, datastar, cowboy, ranch, crypto], + case [A || A <- Names, not lists:member(A, System)] of + [A | _] -> A; + [] -> nova_liveboard + end. + +%% Tracing is "on" if the plugin is in Nova's config, or the tracer has already +%% captured something (covers runtime registration). +tracing_enabled() -> + plugin_in_config() orelse safe_active(). + +plugin_in_config() -> + lists:any( + fun + ({_Type, Mod, _Opts}) -> Mod =:= nova_liveboard_trace_plugin; + (_) -> false + end, + application:get_env(nova, plugins, []) + ). + +safe_active() -> + whereis(nova_liveboard_tracer) =/= undefined andalso nova_liveboard_tracer:active(). + +kura_repos() -> + [nova_liveboard_data:kura_repo_info(R) || R <- nova_liveboard_data:kura_repos()]. + +kura_schemas() -> + lists:append([nova_liveboard_data:kura_schemas(R) || R <- nova_liveboard_data:kura_repos()]). diff --git a/src/nova_liveboard_ports_view.erl b/src/nova_liveboard_ports_view.erl deleted file mode 100644 index c5ab8a3..0000000 --- a/src/nova_liveboard_ports_view.erl +++ /dev/null @@ -1,68 +0,0 @@ --module(nova_liveboard_ports_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_info/2]). - -mount(_Arg, _Req) -> - case arizona_live:is_connected(self()) of - true -> erlang:send_after(3000, self(), refresh); - false -> ok - end, - Ports = nova_liveboard_data:port_info(), - Prefix = nova_liveboard:prefix(), - Bindings = #{ - id => ~"ports_view", - ports => Ports - }, - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"ports", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - Ports = arizona_template:get_binding(ports, Bindings), - arizona_template:from_html( - ~"""" -
-

{integer_to_binary(length(Ports))} open ports · Auto-refreshes every 3s

-
- - - - - - - - - - - - {arizona_template:render_list(fun(Port) -> - arizona_template:from_html(~""" - - - - - - - - """) - end, Ports)} - -
PortNameConnectedInputOutput
{maps:get(id, Port)}{maps:get(name, Port)}{maps:get(connected, Port)}{nova_liveboard_data:format_bytes(maps:get(input, Port))}{nova_liveboard_data:format_bytes(maps:get(output, Port))}
-
-
- """" - ). - -handle_info(refresh, View) -> - erlang:send_after(3000, self(), refresh), - Ports = nova_liveboard_data:port_info(), - State = arizona_view:get_state(View), - UpdatedState = arizona_stateful:put_binding(ports, Ports, State), - {[], arizona_view:update_state(UpdatedState, View)}. diff --git a/src/nova_liveboard_processes_view.erl b/src/nova_liveboard_processes_view.erl deleted file mode 100644 index 6ebe650..0000000 --- a/src/nova_liveboard_processes_view.erl +++ /dev/null @@ -1,109 +0,0 @@ --module(nova_liveboard_processes_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_event/3, handle_info/2]). - -mount(_Arg, _Req) -> - case arizona_live:is_connected(self()) of - true -> erlang:send_after(2000, self(), refresh); - false -> ok - end, - SortBy = memory, - Procs = nova_liveboard_data:top_processes(SortBy, 50), - Prefix = nova_liveboard:prefix(), - Bindings = #{ - id => ~"processes_view", - processes => Procs, - sort_by => SortBy - }, - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"processes", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - Procs = arizona_template:get_binding(processes, Bindings), - SortBy = arizona_template:get_binding(sort_by, Bindings), - SortMem = ~"arizona.pushEvent('sort', {by: 'memory'})", - SortRed = ~"arizona.pushEvent('sort', {by: 'reductions'})", - SortMsg = ~"arizona.pushEvent('sort', {by: 'msgq'})", - arizona_template:from_html( - ~"""" -
-

Top 50 processes · Auto-refreshes every 2s

-
- - - - - - - - - - - - - - {arizona_template:render_list(fun(Proc) -> - MsgQ = maps:get(message_queue_len, Proc), - MsgClass = case MsgQ > 100 of true -> ~"text-amber"; false -> ~"" end, - arizona_template:from_html(~""" - - - - - - - - - - """) - end, Procs)} - -
PIDName / RegisteredCurrent Function - - - - - - Status
{maps:get(pid, Proc)}{maps:get(name, Proc)}{maps:get(current_function, Proc)}{nova_liveboard_data:format_bytes(maps:get(memory, Proc))}{nova_liveboard_data:format_number(maps:get(reductions, Proc))}{integer_to_binary(MsgQ)}{maps:get(status, Proc)}
-
-
- """" - ). - -handle_event(~"sort", Params, View) -> - SortBy = - case maps:get(~"by", Params) of - ~"memory" -> memory; - ~"reductions" -> reductions; - ~"msgq" -> message_queue_len; - _ -> memory - end, - Procs = nova_liveboard_data:top_processes(SortBy, 50), - State = arizona_view:get_state(View), - S1 = arizona_stateful:put_binding(sort_by, SortBy, State), - S2 = arizona_stateful:put_binding(processes, Procs, S1), - {[], arizona_view:update_state(S2, View)}. - -handle_info(refresh, View) -> - erlang:send_after(2000, self(), refresh), - State = arizona_view:get_state(View), - SortBy = arizona_stateful:get_binding(sort_by, State), - Procs = nova_liveboard_data:top_processes(SortBy, 50), - UpdatedState = arizona_stateful:put_binding(processes, Procs, State), - {[], arizona_view:update_state(UpdatedState, View)}. - -%% Internal - -sort_class(Current, Col) when Current =:= Col -> ~"active"; -sort_class(_, _) -> ~"". diff --git a/src/nova_liveboard_router.erl b/src/nova_liveboard_router.erl index 57acd57..5376615 100644 --- a/src/nova_liveboard_router.erl +++ b/src/nova_liveboard_router.erl @@ -3,14 +3,44 @@ -export([routes/1]). +%% All pages, the per-region SSE streams, the request-trace actions and the +%% static assets run on Nova's own listener under the configured prefix. The +%% /sse/:page route returns {stream, ...}, held open by nova_liveboard_sse +%% (see that module + novaframework/nova#387). routes(_Env) -> [ #{ - prefix => nova_liveboard:prefix(), + prefix => binary_to_list(nova_liveboard:prefix()), security => false, routes => [ - {~"/", fun nova_liveboard_controller:index/1, #{methods => [get]}}, - {~"/:page", fun nova_liveboard_controller:index/1, #{methods => [get]}}, + {"/", fun nova_liveboard_page_controller:index/1, #{methods => [get]}}, + {"/processes", fun nova_liveboard_page_controller:processes/1, #{methods => [get]}}, + {"/ets", fun nova_liveboard_page_controller:ets/1, #{methods => [get]}}, + {"/applications", fun nova_liveboard_page_controller:applications/1, #{ + methods => [get] + }}, + {"/ports", fun nova_liveboard_page_controller:ports/1, #{methods => [get]}}, + {"/supervisors", fun nova_liveboard_page_controller:supervisors/1, #{ + methods => [get] + }}, + {"/metrics", fun nova_liveboard_page_controller:metrics/1, #{methods => [get]}}, + {"/database", fun nova_liveboard_page_controller:database/1, #{methods => [get]}}, + {"/schemas", fun nova_liveboard_page_controller:schemas/1, #{methods => [get]}}, + {"/requests", fun nova_liveboard_page_controller:requests/1, #{methods => [get]}}, + {"/requests/trace/start", fun nova_liveboard_action_controller:trace_start/1, #{ + methods => [post] + }}, + {"/requests/trace/stop", fun nova_liveboard_action_controller:trace_stop/1, #{ + methods => [post] + }}, + {"/requests/clear", fun nova_liveboard_action_controller:clear/1, #{ + methods => [post] + }}, + {"/requests/:id", fun nova_liveboard_page_controller:request_show/1, #{ + methods => [get] + }}, + {"/sse/:page", fun nova_liveboard_sse:stream/1, #{methods => [get]}}, + {"/heartbeat", fun(_) -> {status, 200} end, #{methods => [get]}}, {"/assets/[...]", "static/assets"} ] } diff --git a/src/nova_liveboard_schemas_view.erl b/src/nova_liveboard_schemas_view.erl deleted file mode 100644 index 4f0dfd6..0000000 --- a/src/nova_liveboard_schemas_view.erl +++ /dev/null @@ -1,136 +0,0 @@ --module(nova_liveboard_schemas_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_info/2]). - -mount(_Arg, _Req) -> - Repos = nova_liveboard_data:kura_repos(), - Schemas = lists:flatmap( - fun(R) -> - nova_liveboard_data:kura_schemas(R) - end, - Repos - ), - PreparedSchemas = [prepare_schema(S) || S <- Schemas], - Prefix = nova_liveboard:prefix(), - Bindings = #{ - id => ~"schemas_view", - schemas => PreparedSchemas - }, - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"schemas", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - Schemas = arizona_template:get_binding(schemas, Bindings), - arizona_template:from_html( - ~"""" -
-

{integer_to_binary(length(Schemas))} schemas

- {arizona_template:render_list(fun(Schema) -> - arizona_template:from_html(~""" -
-
- {maps:get(module, Schema)} - {maps:get(table, Schema)} -
- {maps:get(fields_html, Schema)} - {maps:get(assocs_html, Schema)} - {maps:get(indexes_html, Schema)} -
- """) - end, Schemas)} -
- """" - ). - -handle_info(_Msg, View) -> - {[], View}. - -%% Internal - -prepare_schema(Schema) -> - #{ - module => maps:get(module, Schema), - table => maps:get(table, Schema), - fields_html => fields_html(maps:get(fields, Schema)), - assocs_html => assocs_html(maps:get(associations, Schema)), - indexes_html => indexes_html(maps:get(indexes, Schema)) - }. - -fields_html([]) -> - ~""; -fields_html(Fields) -> - Header = - <<"", "", - "">>, - Rows = [field_row(F) || F <- Fields], - Footer = ~"
FieldTypePKVirtual
", - iolist_to_binary([Header, Rows, Footer]). - -field_row(F) -> - PK = bool_badge(maps:get(primary_key, F)), - Virtual = bool_badge(maps:get(virtual, F)), - iolist_to_binary([ - ~"", - maps:get(name, F), - ~"", - maps:get(type, F), - ~"", - PK, - ~"", - Virtual, - ~"" - ]). - -assocs_html([]) -> - ~""; -assocs_html(Assocs) -> - Header = - <<"
Associations
", - "", "", - "">>, - Rows = [assoc_row(A) || A <- Assocs], - Footer = ~"
NameTypeSchema
", - iolist_to_binary([Header, Rows, Footer]). - -assoc_row(A) -> - iolist_to_binary([ - ~"", - maps:get(name, A), - ~"", - maps:get(type, A), - ~"", - maps:get(schema, A), - ~"" - ]). - -indexes_html([]) -> - ~""; -indexes_html(Indexes) -> - Header = - <<"
Indexes
", "", - "", "">>, - Rows = [index_row(I) || I <- Indexes], - Footer = ~"
ColumnsUnique
", - iolist_to_binary([Header, Rows, Footer]). - -index_row(I) -> - Cols = iolist_to_binary(lists:join(~", ", maps:get(columns, I))), - Unique = bool_badge(maps:get(unique, I)), - iolist_to_binary([ - ~"", - Cols, - ~"", - Unique, - ~"" - ]). - -bool_badge(true) -> ~"yes"; -bool_badge(false) -> ~"". diff --git a/src/nova_liveboard_sse.erl b/src/nova_liveboard_sse.erl new file mode 100644 index 0000000..f91c1c5 --- /dev/null +++ b/src/nova_liveboard_sse.erl @@ -0,0 +1,170 @@ +-module(nova_liveboard_sse). +-moduledoc """ +The liveboard's live transport: one long-lived SSE stream per page region. + +`stream/1` is a normal Nova controller returning `{stream, Code, Headers, +Source}`. That tuple is picked up by a return-handler we register +(`handle_stream/3`), which `stream_reply`s the SSE headers and then **holds the +connection**, pushing `datastar:patch_elements/2` frames and never returning - +so Nova's buffered `render_response` (which would reply again and crash) never +runs. On client disconnect the next `stream_body` fails and the request process +exits, dropping any tracer subscription. See novaframework/nova#387. + +Two stream shapes: + +- **polled** (vitals/overview/processes/ets/applications/ports/supervisors/ + metrics): a `receive ... after RefreshMs` timer repaints the region. The + metrics stream threads the `nova_liveboard_data:collect_metrics/1` + accumulator through its loop so sparklines and scheduler deltas build up. +- **event-driven** (requests): subscribes to `nova_liveboard_tracer` and + prepends each request as it completes; no polling. + +The handler is registered as an explicit `fun/3` because current Nova invokes +return-handlers with 3 args, while its `{Mod, Fun}` form wraps to arity 4. +""". + +-export([register/0, stream/1, handle_stream/3]). + +-define(PAGE, ~"#page"). +-define(VITALS, ~"#vitals"). + +-spec register() -> ok | {error, atom()}. +register() -> + nova_handlers:register_handler(stream, fun ?MODULE:handle_stream/3). + +%% Nova controller: GET /sse/:page +stream(Req) -> + Page = maps:get(~"page", maps:get(bindings, Req, #{}), ~"overview"), + Qs = cowboy_req:parse_qs(Req), + {stream, 200, headers(), {Page, Qs}}. + +handle_stream({stream, Code, Headers, Source}, _Callback, Req0) -> + Req = cowboy_req:stream_reply(Code, Headers, Req0), + serve(Source, Req). + +%% --------------------------------------------------------------------------- +%% per-page streams +%% --------------------------------------------------------------------------- + +serve({~"vitals", _Qs}, Req) -> + loop_poll(vitals, undefined, Req); +serve({~"metrics", _Qs}, Req) -> + State = nova_liveboard_data:collect_metrics(undefined), + case send(Req, page_inner(nova_liveboard_html:metrics_html(State))) of + ok -> loop_metrics(State, Req); + stop -> ok + end; +serve({~"requests", _Qs}, Req) -> + ok = nova_liveboard_tracer:subscribe(), + send(Req, page_frame(requests, undefined)), + loop_requests(Req); +serve({~"processes", Qs}, Req) -> + loop_poll(processes, sort(Qs), Req); +serve({~"supervisors", Qs}, Req) -> + loop_poll(supervisors, app(Qs), Req); +serve({Page, _Qs}, Req) -> + loop_poll(nova_liveboard_page_controller:page_atom(Page), undefined, Req). + +loop_poll(Page, Opt, Req) -> + receive + _ -> loop_poll(Page, Opt, Req) + after refresh() -> + case send(Req, region_frame(Page, Opt)) of + ok -> loop_poll(Page, Opt, Req); + stop -> ok + end + end. + +%% Metrics needs a stateful accumulator; carry it through the loop. +loop_metrics(State, Req) -> + receive + after refresh() -> + State1 = nova_liveboard_data:collect_metrics(State), + case send(Req, page_inner(nova_liveboard_html:metrics_html(State1))) of + ok -> loop_metrics(State1, Req); + stop -> ok + end + end. + +loop_requests(Req) -> + receive + {nova_liveboard_request, R} -> + ok = send(Req, prepend_request(R)), + ok = send(Req, toolbar_frame()), + loop_requests(Req); + {nova_liveboard_trace_state, _TS} -> + ok = send(Req, toolbar_frame()), + loop_requests(Req); + nova_liveboard_requests_cleared -> + ok = send(Req, page_frame(requests, undefined)), + loop_requests(Req); + _ -> + loop_requests(Req) + end. + +%% --------------------------------------------------------------------------- +%% frames +%% --------------------------------------------------------------------------- + +region_frame(vitals, _) -> + datastar:patch_elements( + nova_liveboard_html:vitals_html(nova_liveboard_data:system_info()), + #{selector => ?VITALS, mode => inner} + ); +region_frame(Page, Opt) -> + page_frame(Page, Opt). + +page_frame(Page, Opt) -> + page_inner(nova_liveboard_page_controller:page_inner(Page, Opt)). + +page_inner(Html) -> + datastar:patch_elements(Html, #{selector => ?PAGE, mode => inner}). + +prepend_request(R) -> + datastar:patch_elements( + nova_liveboard_html:request_row(R), + #{selector => ~"#req-feed", mode => prepend} + ). + +toolbar_frame() -> + datastar:patch_elements( + nova_liveboard_html:requests_toolbar_html(nova_liveboard_tracer:trace_state()), + #{selector => ~"#req-toolbar", mode => inner} + ). + +%% --------------------------------------------------------------------------- +%% transport + query parsing +%% --------------------------------------------------------------------------- + +send(Req, Frame) -> + try + ok = cowboy_req:stream_body(Frame, nofin, Req), + ok + catch + _:_ -> stop + end. + +headers() -> + maps:from_list(datastar:sse_headers()). + +refresh() -> + nova_liveboard:refresh_ms(). + +sort(Qs) -> + case proplists:get_value(~"sort", Qs) of + ~"reductions" -> reductions; + ~"message_queue_len" -> message_queue_len; + _ -> memory + end. + +app(Qs) -> + Names = nova_liveboard_page_controller:app_names(), + case proplists:get_value(~"app", Qs) of + undefined -> + nova_liveboard_page_controller:default_app(Names); + Bin -> + case lists:search(fun(A) -> atom_to_binary(A) =:= Bin end, Names) of + {value, A} -> A; + false -> nova_liveboard_page_controller:default_app(Names) + end + end. diff --git a/src/nova_liveboard_sup.erl b/src/nova_liveboard_sup.erl new file mode 100644 index 0000000..8c4a241 --- /dev/null +++ b/src/nova_liveboard_sup.erl @@ -0,0 +1,21 @@ +-module(nova_liveboard_sup). +-moduledoc "Top supervisor: owns the request tracer.". +-behaviour(supervisor). + +-export([start_link/0, init/1]). + +-spec start_link() -> {ok, pid()}. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +init([]) -> + Children = [ + #{ + id => nova_liveboard_tracer, + start => {nova_liveboard_tracer, start_link, []}, + restart => permanent, + shutdown => 5000, + type => worker + } + ], + {ok, {#{strategy => one_for_one, intensity => 5, period => 10}, Children}}. diff --git a/src/nova_liveboard_sup_view.erl b/src/nova_liveboard_sup_view.erl deleted file mode 100644 index 4335f8a..0000000 --- a/src/nova_liveboard_sup_view.erl +++ /dev/null @@ -1,132 +0,0 @@ --module(nova_liveboard_sup_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_event/3, handle_info/2]). - -mount(_Arg, _Req) -> - case arizona_live:is_connected(self()) of - true -> erlang:send_after(5000, self(), refresh); - false -> ok - end, - Apps = [Name || {Name, _, _} <- lists:sort(application:which_applications())], - SelectedApp = - case Apps of - [First | _] -> First; - [] -> undefined - end, - Tree = fetch_tree(SelectedApp), - Prefix = nova_liveboard:prefix(), - Bindings = #{ - id => ~"sup_view", - apps => Apps, - selected_app => SelectedApp, - selected_app_name => atom_to_binary(SelectedApp), - flat_tree => flatten_tree(Tree) - }, - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"supervisors", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - arizona_template:from_html( - ~""""" -
-

Auto-refreshes every 5s

- -
- {arizona_template:render_list(fun(App) -> - AppBin = atom_to_binary(App), - BtnClass = case App =:= arizona_template:get_binding(selected_app, Bindings) of - true -> ~"app-btn active"; - false -> ~"app-btn" - end, - OnClick = <<"arizona.pushEvent('select_app', {app: '", AppBin/binary, "'})">>, - arizona_template:from_html(~""" - - """) - end, arizona_template:get_binding(apps, Bindings))} -
- -
-
Supervision Tree — {arizona_template:get_binding(selected_app_name, Bindings)}
-
- {arizona_template:render_list(fun(Node) -> - Name = maps:get(name, Node), - Pid = maps:get(pid, Node), - Type = maps:get(type, Node), - Memory = maps:get(memory, Node, 0), - MsgQ = maps:get(message_queue_len, Node, 0), - Status = maps:get(status, Node, ~"unknown"), - Depth = maps:get(depth, Node), - Indent = integer_to_binary(Depth * 24), - TypeBadge = type_badge(Type), - TypeLabel = type_label(Type), - MsgClass = case MsgQ > 100 of true -> ~"text-amber"; false -> ~"" end, - arizona_template:from_html(~""" -
- {TypeLabel} - {Name} - - {Pid} - {nova_liveboard_data:format_bytes(Memory)} - msgq: {integer_to_binary(MsgQ)} - {Status} - -
- """) - end, arizona_template:get_binding(flat_tree, Bindings))} -
-
-
- """"" - ). - -handle_event(~"select_app", Params, View) -> - AppBin = maps:get(~"app", Params), - App = binary_to_existing_atom(AppBin), - Tree = fetch_tree(App), - State = arizona_view:get_state(View), - S1 = arizona_stateful:put_binding(selected_app, App, State), - S2 = arizona_stateful:put_binding(selected_app_name, AppBin, S1), - S3 = arizona_stateful:put_binding(flat_tree, flatten_tree(Tree), S2), - {[], arizona_view:update_state(S3, View)}. - -handle_info(refresh, View) -> - erlang:send_after(5000, self(), refresh), - State = arizona_view:get_state(View), - App = arizona_stateful:get_binding(selected_app, State), - Tree = fetch_tree(App), - UpdatedState = arizona_stateful:put_binding(flat_tree, flatten_tree(Tree), State), - {[], arizona_view:update_state(UpdatedState, View)}. - -%% Internal - -fetch_tree(undefined) -> - []; -fetch_tree(App) -> - case nova_liveboard_data:supervision_tree(App) of - {ok, Tree} -> Tree; - {error, _} -> [] - end. - -flatten_tree(Nodes) -> - lists:reverse(flatten_tree(Nodes, 0, [])). - -flatten_tree([], _Depth, Acc) -> - Acc; -flatten_tree([#{children := Children} = Node | Rest], Depth, Acc) -> - FlatNode = Node#{depth => Depth}, - Acc1 = flatten_tree(Children, Depth + 1, [FlatNode | Acc]), - flatten_tree(Rest, Depth, Acc1). - -type_badge(supervisor) -> ~"badge badge-blue"; -type_badge(_) -> ~"badge badge-green". - -type_label(supervisor) -> ~"sup"; -type_label(_) -> ~"worker". diff --git a/src/nova_liveboard_system_view.erl b/src/nova_liveboard_system_view.erl deleted file mode 100644 index 5bc5ffe..0000000 --- a/src/nova_liveboard_system_view.erl +++ /dev/null @@ -1,153 +0,0 @@ --module(nova_liveboard_system_view). --behaviour(arizona_view). --compile({parse_transform, arizona_parse_transform}). - --export([mount/2, render/1, handle_info/2]). - -mount(_Arg, _Req) -> - case arizona_live:is_connected(self()) of - true -> erlang:send_after(2000, self(), refresh); - false -> ok - end, - Info = nova_liveboard_data:system_info(), - Prefix = nova_liveboard:prefix(), - Bindings = #{ - id => ~"system_view", - info => Info - }, - Layout = - {nova_liveboard_layout, render, main_content, #{ - active_page => ~"system", - prefix => Prefix, - ws_path => <<(arizona_nova:prefix())/binary, "/live">>, - arizona_prefix => arizona_nova:prefix() - }}, - arizona_view:new(?MODULE, Bindings, Layout). - -render(Bindings) -> - Info = arizona_template:get_binding(info, Bindings), - Mem = maps:get(memory, Info), - arizona_template:from_html( - ~""" -
-

Auto-refreshes every 2s

- -
-
-
OTP Release
-
{maps:get(otp_release, Info)}
-
ERTS {maps:get(erts_version, Info)}
-
-
-
Uptime
-
{nova_liveboard_data:format_uptime(maps:get(uptime, Info))}
-
{maps:get(system_architecture, Info)}
-
-
-
Schedulers
-
{integer_to_binary(maps:get(scheduler_online, Info))}
-
of {integer_to_binary(maps:get(scheduler_count, Info))} available
-
-
-
Total Memory
-
{nova_liveboard_data:format_bytes(maps:get(total, Mem))}
-
-
- -
-
-
Processes
-
{nova_liveboard_data:format_number(maps:get(process_count, Info))}
-
limit: {nova_liveboard_data:format_number(maps:get(process_limit, Info))}
-
-
-
-
-
-
Atoms
-
{nova_liveboard_data:format_number(maps:get(atom_count, Info))}
-
limit: {nova_liveboard_data:format_number(maps:get(atom_limit, Info))}
-
-
-
-
-
-
Ports
-
{nova_liveboard_data:format_number(maps:get(port_count, Info))}
-
limit: {nova_liveboard_data:format_number(maps:get(port_limit, Info))}
-
-
-
-
-
-
ETS Tables
-
{integer_to_binary(maps:get(ets_count, Info))}
-
-
- -
-
Memory Breakdown
- - - - - - - - - - - {render_mem_row(~"Processes", maps:get(processes_used, Mem), maps:get(total, Mem))} - {render_mem_row(~"Binary", maps:get(binary, Mem), maps:get(total, Mem))} - {render_mem_row(~"Code", maps:get(code, Mem), maps:get(total, Mem))} - {render_mem_row(~"ETS", maps:get(ets, Mem), maps:get(total, Mem))} - {render_mem_row(~"Atom", maps:get(atom_used, Mem), maps:get(total, Mem))} - {render_mem_row(~"System", maps:get(system, Mem), maps:get(total, Mem))} - -
TypeSize% of TotalUsage
-
-
- """ - ). - -handle_info(refresh, View) -> - erlang:send_after(2000, self(), refresh), - Info = nova_liveboard_data:system_info(), - State = arizona_view:get_state(View), - UpdatedState = arizona_stateful:put_binding(info, Info, State), - {[], arizona_view:update_state(UpdatedState, View)}. - -%% Internal - -usage_pct(Used, Limit) when Limit > 0 -> - Pct = (Used * 100) div Limit, - integer_to_binary(min(100, Pct)); -usage_pct(_, _) -> - ~"0". - -render_mem_row(Label, Value, Total) -> - Pct = - case Total of - 0 -> 0; - _ -> (Value * 100) div Total - end, - Color = - if - Pct > 60 -> ~"bar-fill-red"; - Pct > 30 -> ~"bar-fill-amber"; - true -> ~"bar-fill-blue" - end, - arizona_template:from_html( - ~""" - - {Label} - {nova_liveboard_data:format_bytes(Value)} - {integer_to_binary(Pct)}% - -
-
-
- - - """ - ). diff --git a/src/nova_liveboard_trace_plugin.erl b/src/nova_liveboard_trace_plugin.erl new file mode 100644 index 0000000..e257c6d --- /dev/null +++ b/src/nova_liveboard_trace_plugin.erl @@ -0,0 +1,152 @@ +-module(nova_liveboard_trace_plugin). +-moduledoc """ +Nova plugin that feeds the liveboard's request tracer. + +Register it as a global plugin in your Nova config to capture every request +handled by the node: + + {nova, [ + {plugins, [ + {pre_request, nova_liveboard_trace_plugin, #{}}, + {post_request, nova_liveboard_trace_plugin, #{}} + ]} + ]}. + +`pre_request/4` stamps the request with an id, start time, reductions baseline +and the resolved handler MFA (from `Env#{callback}`), and - when deep trace is +armed - turns on scoped process tracing of the handling process. +`post_request/4` computes duration / reductions / peak memory / status and +hands the summary to `nova_liveboard_tracer`. The whole chain runs in one +process, so the marker stashed in `Req` survives from pre to post. + +The dashboard's own routes are skipped so it never traces itself (its SSE +streams never return, and would otherwise never finalise). +""". + +-behaviour(nova_plugin). + +-export([init/0, pre_request/4, post_request/4, plugin_info/0]). + +-define(MARK, '$nova_liveboard_trace'). + +-spec init() -> {ok, undefined}. +init() -> + {ok, undefined}. + +pre_request(Req, Env, _Opts, State) -> + case skip(Req) of + true -> + {ok, Req, State}; + false -> + ReqId = new_id(), + Traced = arm(ReqId), + Mark = #{ + id => ReqId, + t0 => erlang:monotonic_time(microsecond), + reds0 => reductions(), + method => cowboy_req:method(Req), + path => cowboy_req:path(Req), + handler => handler_mfa(Env), + traced => Traced + }, + {ok, Req#{?MARK => Mark}, State} + end. + +post_request(Req, _Env, _Opts, State) -> + case maps:get(?MARK, Req, undefined) of + undefined -> + {ok, Req, State}; + Mark -> + finalize(Req, Mark), + {ok, Req, State} + end. + +plugin_info() -> + #{ + title => ~"Nova Liveboard request tracer", + version => ~"1.0.0", + url => ~"https://github.com/novaframework/nova_liveboard", + authors => [~"nova_liveboard"], + description => + ~"Captures per-request timing, reductions and (in deep mode) the tree of processes spawned while handling each request.", + options => [] + }. + +%% --------------------------------------------------------------------------- +%% internal +%% --------------------------------------------------------------------------- + +finalize(Req, Mark) -> + #{id := ReqId, t0 := T0, reds0 := Reds0, traced := Traced} = Mark, + Traced andalso untrace_self(), + Summary = #{ + id => ReqId, + method => maps:get(method, Mark), + path => maps:get(path, Mark), + handler => maps:get(handler, Mark), + status => maps:get(resp_status_code, Req, undefined), + duration_us => erlang:monotonic_time(microsecond) - T0, + reductions => max(0, reductions() - Reds0), + mem => mem(), + ts => erlang:system_time(millisecond) + }, + nova_liveboard_tracer:finalize(ReqId, Summary). + +untrace_self() -> + try + erlang:trace(self(), false, [procs, set_on_spawn]) + catch + _:_ -> 0 + end. + +%% Arm scoped tracing of the current (request-handling) process when the +%% tracer says deep trace is on. set_on_spawn makes any process this one +%% spawns inherit the trace, so the whole spawned subtree is captured. +arm(ReqId) -> + case whereis(nova_liveboard_tracer) of + undefined -> + false; + Tracer -> + case nova_liveboard_tracer:maybe_arm(ReqId, self()) of + trace -> + erlang:trace(self(), true, [procs, set_on_spawn, {tracer, Tracer}]), + true; + no_trace -> + false + end + end. + +skip(Req) -> + whereis(nova_liveboard_tracer) =:= undefined orelse + own_path(cowboy_req:path(Req)). + +own_path(Path) -> + Prefix = nova_liveboard:prefix(), + case binary:match(Path, Prefix) of + {0, _} -> true; + _ -> false + end. + +handler_mfa(#{callback := Fun}) when is_function(Fun) -> + Info = erlang:fun_info(Fun), + M = proplists:get_value(module, Info, unknown), + F = proplists:get_value(name, Info, unknown), + A = proplists:get_value(arity, Info, 0), + iolist_to_binary(io_lib:format("~s:~s/~b", [M, F, A])); +handler_mfa(#{callback := {M, F}}) -> + iolist_to_binary(io_lib:format("~s:~s", [M, F])); +handler_mfa(_) -> + ~"unknown". + +reductions() -> + {reductions, R} = erlang:process_info(self(), reductions), + R. + +mem() -> + case erlang:process_info(self(), memory) of + {memory, M} -> M; + undefined -> 0 + end. + +new_id() -> + string:lowercase(binary:encode_hex(crypto:strong_rand_bytes(6))). diff --git a/src/nova_liveboard_tracer.erl b/src/nova_liveboard_tracer.erl new file mode 100644 index 0000000..8a57a82 --- /dev/null +++ b/src/nova_liveboard_tracer.erl @@ -0,0 +1,246 @@ +-module(nova_liveboard_tracer). +-moduledoc """ +Request store and deep-trace collector for the liveboard. + +Two jobs in one `gen_server`: + +1. **Store** - a capped, newest-first ring of recently completed requests + (`nova_liveboard:request_buffer/0` deep). `nova_liveboard_trace_plugin` + calls `finalize/2` from each request's `post_request` hook; SSE feeds + `subscribe/0` to it for live updates. + +2. **Trace collector** - when deep trace is armed (`start_trace/1`), the next + N requests have their handling process traced with + `erlang:trace(self(), true, [procs, set_on_spawn, {tracer, Pid}])` (the + plugin enables it; this server is the `Pid`). It receives + `{trace, Parent, spawn, Child, MFA}` messages, attributes each spawned + process to the owning request by walking the parent chain, and attaches the + resulting spawned-process tree to the request record on `finalize/2`. + +The dashboard works without any of this; only the Requests page needs the +plugin registered. +""". + +-behaviour(gen_server). + +-export([ + start_link/0, + maybe_arm/2, + finalize/2, + recent/0, + get/1, + clear/0, + subscribe/0, + start_trace/1, + stop_trace/0, + trace_state/0, + active/0 +]). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2]). + +-define(SERVER, ?MODULE). + +-record(state, { + requests = [] :: [map()], + cap = 200 :: pos_integer(), + armed = 0 :: non_neg_integer(), + active = false :: boolean(), + subs = #{} :: #{reference() => pid()}, + %% in-flight deep traces, keyed by request id + traces = #{} :: #{binary() => trace_acc()}, + %% any currently-traced pid -> the request id that owns it + pid2req = #{} :: #{pid() => binary()} +}). + +-type trace_acc() :: #{nodes := [map()], depths := #{pid() => non_neg_integer()}}. + +%% --------------------------------------------------------------------------- +%% api +%% --------------------------------------------------------------------------- + +-spec start_link() -> {ok, pid()}. +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +-doc """ +Ask whether the request `ReqId` handled by `RootPid` should be deep-traced. + +Returns `trace` (and registers the root pid) when deep trace is armed, else +`no_trace`. The caller (the request process) is responsible for calling +`erlang:trace/3` on itself when `trace` is returned. +""". +-spec maybe_arm(binary(), pid()) -> trace | no_trace. +maybe_arm(ReqId, RootPid) -> + gen_server:call(?SERVER, {maybe_arm, ReqId, RootPid}). + +-doc "Record a completed request; attaches the spawned tree if it was traced.". +-spec finalize(binary(), map()) -> ok. +finalize(ReqId, Summary) -> + gen_server:cast(?SERVER, {finalize, ReqId, Summary}). + +-spec recent() -> [map()]. +recent() -> gen_server:call(?SERVER, recent). + +-spec get(binary()) -> map() | undefined. +get(ReqId) -> gen_server:call(?SERVER, {get, ReqId}). + +-spec clear() -> ok. +clear() -> gen_server:cast(?SERVER, clear). + +-doc "Register the caller for live request + trace-state notifications.". +-spec subscribe() -> ok. +subscribe() -> gen_server:call(?SERVER, {subscribe, self()}). + +-spec start_trace(pos_integer()) -> ok. +start_trace(N) -> gen_server:cast(?SERVER, {start_trace, N}). + +-spec stop_trace() -> ok. +stop_trace() -> gen_server:cast(?SERVER, stop_trace). + +-spec trace_state() -> map(). +trace_state() -> gen_server:call(?SERVER, trace_state). + +-doc "Whether any request has been captured (a proxy for 'plugin is wired up').". +-spec active() -> boolean(). +active() -> gen_server:call(?SERVER, active). + +%% --------------------------------------------------------------------------- +%% gen_server +%% --------------------------------------------------------------------------- + +init([]) -> + {ok, #state{cap = nova_liveboard:request_buffer()}}. + +handle_call({maybe_arm, ReqId, RootPid}, _From, #state{armed = N} = S) when N > 0 -> + Acc = #{nodes => [], depths => #{RootPid => 0}}, + S1 = S#state{ + armed = N - 1, + active = true, + traces = (S#state.traces)#{ReqId => Acc}, + pid2req = (S#state.pid2req)#{RootPid => ReqId} + }, + {reply, trace, notify_trace_state(S1)}; +handle_call({maybe_arm, _ReqId, _RootPid}, _From, S) -> + {reply, no_trace, S#state{active = true}}; +handle_call(recent, _From, S) -> + {reply, S#state.requests, S}; +handle_call({get, ReqId}, _From, S) -> + Found = + case lists:search(fun(R) -> maps:get(id, R) =:= ReqId end, S#state.requests) of + {value, R} -> R; + false -> undefined + end, + {reply, Found, S}; +handle_call({subscribe, Pid}, _From, S) -> + Ref = erlang:monitor(process, Pid), + {reply, ok, S#state{subs = (S#state.subs)#{Ref => Pid}}}; +handle_call(trace_state, _From, S) -> + {reply, trace_state_map(S), S}; +handle_call(active, _From, S) -> + {reply, S#state.active, S}; +handle_call(_Req, _From, S) -> + {reply, ok, S}. + +handle_cast({finalize, ReqId, Summary}, S) -> + {Tree, S1} = take_trace(ReqId, S), + Record = Summary#{spawned => length(Tree), spawned_tree => Tree}, + Reqs = lists:sublist([Record | S1#state.requests], S1#state.cap), + S2 = S1#state{requests = Reqs, active = true}, + notify(S2, {nova_liveboard_request, Record}), + {noreply, S2}; +handle_cast(clear, S) -> + notify(S, nova_liveboard_requests_cleared), + {noreply, S#state{requests = []}}; +handle_cast({start_trace, N}, S) -> + {noreply, notify_trace_state(S#state{armed = N})}; +handle_cast(stop_trace, S) -> + {noreply, notify_trace_state(S#state{armed = 0})}; +handle_cast(_Msg, S) -> + {noreply, S}. + +handle_info({trace, Parent, spawn, Child, MFA}, S) -> + {noreply, record_spawn(Parent, Child, MFA, S)}; +handle_info({'DOWN', Ref, process, _Pid, _Reason}, S) -> + {noreply, S#state{subs = maps:remove(Ref, S#state.subs)}}; +handle_info(_Msg, S) -> + {noreply, S}. + +%% --------------------------------------------------------------------------- +%% trace bookkeeping +%% --------------------------------------------------------------------------- + +record_spawn(Parent, Child, MFA, S) -> + case maps:find(Parent, S#state.pid2req) of + {ok, ReqId} -> + Acc = maps:get(ReqId, S#state.traces), + Depths = maps:get(depths, Acc), + Depth = maps:get(Parent, Depths, 0) + 1, + Node = #{ + pid => pid_bin(Child), + mfa => format_mfa(MFA), + parent => pid_bin(Parent), + depth => Depth + }, + Acc1 = Acc#{ + nodes => [Node | maps:get(nodes, Acc)], + depths => Depths#{Child => Depth} + }, + S#state{ + traces = (S#state.traces)#{ReqId => Acc1}, + pid2req = (S#state.pid2req)#{Child => ReqId} + }; + error -> + S + end. + +take_trace(ReqId, S) -> + case maps:take(ReqId, S#state.traces) of + {Acc, Traces1} -> + Pids = maps:keys(maps:get(depths, Acc)), + untrace(Pids), + Pid2Req1 = maps:without(Pids, S#state.pid2req), + Tree = lists:reverse(maps:get(nodes, Acc)), + {Tree, S#state{traces = Traces1, pid2req = Pid2Req1}}; + error -> + {[], S} + end. + +untrace(Pids) -> + lists:foreach( + fun(Pid) -> + try + erlang:trace(Pid, false, [procs, set_on_spawn]) + catch + _:_ -> 0 + end + end, + Pids + ). + +%% --------------------------------------------------------------------------- +%% notifications +%% --------------------------------------------------------------------------- + +notify(#state{subs = Subs}, Msg) -> + maps:foreach(fun(_Ref, Pid) -> Pid ! Msg end, Subs). + +notify_trace_state(S) -> + notify(S, {nova_liveboard_trace_state, trace_state_map(S)}), + S. + +trace_state_map(#state{armed = N}) -> + #{armed => N > 0, remaining => N}. + +%% --------------------------------------------------------------------------- +%% helpers +%% --------------------------------------------------------------------------- + +pid_bin(Pid) -> list_to_binary(pid_to_list(Pid)). + +format_mfa({M, F, Args}) when is_list(Args) -> + iolist_to_binary(io_lib:format("~s:~s/~b", [M, F, length(Args)])); +format_mfa({M, F, A}) when is_integer(A) -> + iolist_to_binary(io_lib:format("~s:~s/~b", [M, F, A])); +format_mfa(Other) -> + iolist_to_binary(io_lib:format("~p", [Other])). diff --git a/test/nova_liveboard_data_tests.erl b/test/nova_liveboard_data_tests.erl new file mode 100644 index 0000000..7e10e9c --- /dev/null +++ b/test/nova_liveboard_data_tests.erl @@ -0,0 +1,64 @@ +-module(nova_liveboard_data_tests). +-include_lib("eunit/include/eunit.hrl"). + +format_bytes_test_() -> + [ + ?_assertEqual(~"512 B", nova_liveboard_data:format_bytes(512)), + ?_assertEqual(~"1.0 KB", nova_liveboard_data:format_bytes(1024)), + ?_assertEqual(~"1.0 MB", nova_liveboard_data:format_bytes(1048576)), + ?_assertEqual(~"1.0 GB", nova_liveboard_data:format_bytes(1073741824)) + ]. + +format_number_test_() -> + [ + ?_assertEqual(~"42", nova_liveboard_data:format_number(42)), + ?_assertEqual(~"1.5K", nova_liveboard_data:format_number(1500)), + ?_assertEqual(~"2.0M", nova_liveboard_data:format_number(2000000)) + ]. + +format_uptime_test_() -> + [ + ?_assertEqual(~"00:00:05", nova_liveboard_data:format_uptime(5000)), + ?_assertEqual(~"00:01:00", nova_liveboard_data:format_uptime(60000)), + ?_assertEqual(~"1d 00:00:00", nova_liveboard_data:format_uptime(86400000)) + ]. + +sparkline_points_test_() -> + [ + ?_assertEqual(~"", nova_liveboard_data:sparkline_points([], 200, 40)), + %% a single point spans the full width at mid-height + ?_assertEqual(~"0,20.0 200.0,20.0", nova_liveboard_data:sparkline_points([5], 200, 40)), + ?_assert(is_binary(nova_liveboard_data:sparkline_points([1, 5, 3, 9, 2], 200, 40))) + ]. + +system_info_shape_test() -> + Sys = nova_liveboard_data:system_info(), + ?assert(is_integer(maps:get(process_count, Sys))), + ?assert(is_integer(maps:get(process_limit, Sys))), + ?assert(is_map(maps:get(memory, Sys))), + ?assert(maps:get(process_count, Sys) =< maps:get(process_limit, Sys)). + +top_processes_shape_test() -> + Procs = nova_liveboard_data:top_processes(memory, 5), + ?assert(length(Procs) =< 5), + ?assert(lists:all(fun(P) -> is_binary(maps:get(pid, P)) end, Procs)), + %% sorted descending by the requested key + Mems = [maps:get(memory, P) || P <- Procs], + ?assertEqual(lists:reverse(lists:sort(Mems)), Mems). + +ets_and_apps_and_ports_shape_test() -> + ?assert(is_list(nova_liveboard_data:ets_tables())), + Apps = nova_liveboard_data:running_applications(), + ?assert(lists:any(fun(A) -> maps:get(name, A) =:= ~"kernel" end, Apps)), + ?assert(is_list(nova_liveboard_data:port_info())). + +supervision_tree_test() -> + {ok, Tree} = nova_liveboard_data:supervision_tree(kernel), + ?assert(is_list(Tree)), + ?assertMatch({error, _}, nova_liveboard_data:supervision_tree(no_such_app_xyz)). + +collect_metrics_accumulates_test() -> + S0 = nova_liveboard_data:collect_metrics(undefined), + S1 = nova_liveboard_data:collect_metrics(S0), + ?assertEqual(2, queue:len(maps:get(total_memory, S1))), + ?assert(is_list(maps:get(scheduler_util, S1))). diff --git a/test/nova_liveboard_html_tests.erl b/test/nova_liveboard_html_tests.erl new file mode 100644 index 0000000..23f3aaa --- /dev/null +++ b/test/nova_liveboard_html_tests.erl @@ -0,0 +1,147 @@ +-module(nova_liveboard_html_tests). +-include_lib("eunit/include/eunit.hrl"). + +bin(IoData) -> iolist_to_binary(IoData). + +contains(Hay, Needle) -> binary:match(bin(Hay), Needle) =/= nomatch. + +count(Hay, Needle) -> length(binary:matches(bin(Hay), Needle)). + +%% ---- escaping ---- + +html_escape_test_() -> + [ + ?_assertEqual(~"&<>"", nova_liveboard_html:html_escape(~"&<>\"")), + ?_assertEqual(~"plain", nova_liveboard_html:html_escape(~"plain")), + ?_assertEqual(~"42", nova_liveboard_html:html_escape(42)), + ?_assertEqual(~"an_atom", nova_liveboard_html:html_escape(an_atom)) + ]. + +escape_blocks_injection_test() -> + Out = bin(nova_liveboard_html:html_escape(~"")), + ?assertNot(contains(Out, ~" + + + + + + + +
+ + + + + + + +
+
+ + +

+ Page not found +

+ +

Sorry, but the page you were trying to get to, does not exist. You +may want to try searching this site using the sidebar + + or using our API Reference page + +to find what you were looking for.

+ +
+
+
+ + + diff --git a/doc/api-reference.html b/doc/api-reference.html new file mode 100644 index 0000000..c739fe1 --- /dev/null +++ b/doc/api-reference.html @@ -0,0 +1,243 @@ + + + + + + + + + + + API Reference — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

API Reference nova_liveboard v#0.0.0+build.11.ref1019ff1

+ + +
+ +
+

Modules

+
+
+ + +

Configuration helpers for the liveboard.

+ +
+
+ + +

POST handlers for the Requests page controls (arm / disarm / clear deep trace).

+ +
+
+ + +

Application entry point.

+ +
+
+ + +
+
+ + +

Pure HTML rendering for the liveboard - "BEAM mission control".

+ +
+
+ + +

Nova controllers for the liveboard pages.

+ +
+ +
+ + +

The liveboard's live transport: one long-lived SSE stream per page region.

+ +
+
+ + +

Top supervisor: owns the request tracer.

+ +
+
+ + +

Nova plugin that feeds the liveboard's request tracer.

+ +
+
+ + +

Request store and deep-trace collector for the liveboard.

+ +
+ +
+
+ + +
+ +
+
+
+ + + diff --git a/doc/dist/html-Y2MUTVIN.js b/doc/dist/html-Y2MUTVIN.js new file mode 100644 index 0000000..b3ec4cd --- /dev/null +++ b/doc/dist/html-Y2MUTVIN.js @@ -0,0 +1,210 @@ +(()=>{var _s=Object.create;var jn=Object.defineProperty;var Ls=Object.getOwnPropertyDescriptor;var Ts=Object.getOwnPropertyNames;var Ps=Object.getPrototypeOf,Os=Object.prototype.hasOwnProperty;var _=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var As=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Ts(t))!Os.call(e,i)&&i!==n&&jn(e,i,{get:()=>t[i],enumerable:!(r=Ls(t,i))||r.enumerable});return e};var Y=(e,t,n)=>(n=e!=null?_s(Ps(e)):{},As(t||!e||!e.__esModule?jn(n,"default",{value:e,enumerable:!0}):n,e));var fr=_((mc,hr)=>{var dr="Expected a function",ur=NaN,Ds="[object Symbol]",Bs=/^\s+|\s+$/g,Qs=/^[-+]0x[0-9a-f]+$/i,qs=/^0b[01]+$/i,Fs=/^0o[0-7]+$/i,Vs=parseInt,$s=typeof global=="object"&&global&&global.Object===Object&&global,Us=typeof self=="object"&&self&&self.Object===Object&&self,js=$s||Us||Function("return this")(),Ws=Object.prototype,zs=Ws.toString,Gs=Math.max,Ks=Math.min,Ht=function(){return js.Date.now()};function Ys(e,t,n){var r,i,s,o,a,l,u=0,c=!1,d=!1,h=!0;if(typeof e!="function")throw new TypeError(dr);t=cr(t)||0,$e(n)&&(c=!!n.leading,d="maxWait"in n,s=d?Gs(cr(n.maxWait)||0,t):s,h="trailing"in n?!!n.trailing:h);function p(S){var I=r,$=i;return r=i=void 0,u=S,o=e.apply($,I),o}function g(S){return u=S,a=setTimeout(w,t),c?p(S):o}function m(S){var I=S-l,$=S-u,ee=t-I;return d?Ks(ee,s-$):ee}function v(S){var I=S-l,$=S-u;return l===void 0||I>=t||I<0||d&&$>=s}function w(){var S=Ht();if(v(S))return x(S);a=setTimeout(w,m(S))}function x(S){return a=void 0,h&&r?p(S):(r=i=void 0,o)}function P(){a!==void 0&&clearTimeout(a),u=0,r=l=i=a=void 0}function Q(){return a===void 0?o:x(Ht())}function V(){var S=Ht(),I=v(S);if(r=arguments,i=this,l=S,I){if(a===void 0)return g(l);if(d)return a=setTimeout(w,t),p(l)}return a===void 0&&(a=setTimeout(w,t)),o}return V.cancel=P,V.flush=Q,V}function Js(e,t,n){var r=!0,i=!0;if(typeof e!="function")throw new TypeError(dr);return $e(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),Ys(e,t,{leading:r,maxWait:t,trailing:i})}function $e(e){var t=typeof e;return!!e&&(t=="object"||t=="function")}function Xs(e){return!!e&&typeof e=="object"}function Zs(e){return typeof e=="symbol"||Xs(e)&&zs.call(e)==Ds}function cr(e){if(typeof e=="number")return e;if(Zs(e))return ur;if($e(e)){var t=typeof e.valueOf=="function"?e.valueOf():e;e=$e(t)?t+"":t}if(typeof e!="string")return e===0?e:+e;e=e.replace(Bs,"");var n=qs.test(e);return n||Fs.test(e)?Vs(e.slice(2),n?2:8):Qs.test(e)?ur:+e}hr.exports=Js});var q=_(H=>{"use strict";H.__esModule=!0;H.extend=Lr;H.indexOf=fo;H.escapeExpression=po;H.isEmpty=mo;H.createFrame=go;H.blockParams=vo;H.appendContextPath=yo;var lo={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`","=":"="},uo=/[&<>"'`=]/g,co=/[&<>"'`=]/;function ho(e){return lo[e]}function Lr(e){for(var t=1;t{"use strict";ze.__esModule=!0;var zt=["description","fileName","lineNumber","endLineNumber","message","name","number","stack"];function Gt(e,t){var n=t&&t.loc,r=void 0,i=void 0,s=void 0,o=void 0;n&&(r=n.start.line,i=n.end.line,s=n.start.column,o=n.end.column,e+=" - "+r+":"+s);for(var a=Error.prototype.constructor.call(this,e),l=0;l{"use strict";Ge.__esModule=!0;var Kt=q();Ge.default=function(e){e.registerHelper("blockHelperMissing",function(t,n){var r=n.inverse,i=n.fn;if(t===!0)return i(this);if(t===!1||t==null)return r(this);if(Kt.isArray(t))return t.length>0?(n.ids&&(n.ids=[n.name]),e.helpers.each(t,n)):r(this);if(n.data&&n.ids){var s=Kt.createFrame(n.data);s.contextPath=Kt.appendContextPath(n.data.contextPath,n.name),n={data:s}}return i(t,n)})};Or.exports=Ge.default});var Cr=_((Ke,Ir)=>{"use strict";Ke.__esModule=!0;function wo(e){return e&&e.__esModule?e:{default:e}}var be=q(),bo=X(),Eo=wo(bo);Ke.default=function(e){e.registerHelper("each",function(t,n){if(!n)throw new Eo.default("Must pass iterator to #each");var r=n.fn,i=n.inverse,s=0,o="",a=void 0,l=void 0;n.data&&n.ids&&(l=be.appendContextPath(n.data.contextPath,n.ids[0])+"."),be.isFunction(t)&&(t=t.call(this)),n.data&&(a=be.createFrame(n.data));function u(g,m,v){a&&(a.key=g,a.index=m,a.first=m===0,a.last=!!v,l&&(a.contextPath=l+g)),o=o+r(t[g],{data:a,blockParams:be.blockParams([t[g],g],[l+g,null])})}if(t&&typeof t=="object")if(be.isArray(t))for(var c=t.length;s{"use strict";Ye.__esModule=!0;function xo(e){return e&&e.__esModule?e:{default:e}}var ko=X(),So=xo(ko);Ye.default=function(e){e.registerHelper("helperMissing",function(){if(arguments.length!==1)throw new So.default('Missing helper: "'+arguments[arguments.length-1].name+'"')})};Rr.exports=Ye.default});var Br=_((Je,Dr)=>{"use strict";Je.__esModule=!0;function _o(e){return e&&e.__esModule?e:{default:e}}var Nr=q(),Lo=X(),Hr=_o(Lo);Je.default=function(e){e.registerHelper("if",function(t,n){if(arguments.length!=2)throw new Hr.default("#if requires exactly one argument");return Nr.isFunction(t)&&(t=t.call(this)),!n.hash.includeZero&&!t||Nr.isEmpty(t)?n.inverse(this):n.fn(this)}),e.registerHelper("unless",function(t,n){if(arguments.length!=2)throw new Hr.default("#unless requires exactly one argument");return e.helpers.if.call(this,t,{fn:n.inverse,inverse:n.fn,hash:n.hash})})};Dr.exports=Je.default});var qr=_((Xe,Qr)=>{"use strict";Xe.__esModule=!0;Xe.default=function(e){e.registerHelper("log",function(){for(var t=[void 0],n=arguments[arguments.length-1],r=0;r{"use strict";Ze.__esModule=!0;Ze.default=function(e){e.registerHelper("lookup",function(t,n,r){return t&&r.lookupProperty(t,n)})};Fr.exports=Ze.default});var Ur=_((et,$r)=>{"use strict";et.__esModule=!0;function To(e){return e&&e.__esModule?e:{default:e}}var Ee=q(),Po=X(),Oo=To(Po);et.default=function(e){e.registerHelper("with",function(t,n){if(arguments.length!=2)throw new Oo.default("#with requires exactly one argument");Ee.isFunction(t)&&(t=t.call(this));var r=n.fn;if(Ee.isEmpty(t))return n.inverse(this);var i=n.data;return n.data&&n.ids&&(i=Ee.createFrame(n.data),i.contextPath=Ee.appendContextPath(n.data.contextPath,n.ids[0])),r(t,{data:i,blockParams:Ee.blockParams([t],[i&&i.contextPath])})})};$r.exports=et.default});var Yt=_(tt=>{"use strict";tt.__esModule=!0;tt.registerDefaultHelpers=Uo;tt.moveHelperToHooks=jo;function se(e){return e&&e.__esModule?e:{default:e}}var Ao=Ar(),Io=se(Ao),Co=Cr(),Ro=se(Co),Mo=Mr(),No=se(Mo),Ho=Br(),Do=se(Ho),Bo=qr(),Qo=se(Bo),qo=Vr(),Fo=se(qo),Vo=Ur(),$o=se(Vo);function Uo(e){Io.default(e),Ro.default(e),No.default(e),Do.default(e),Qo.default(e),Fo.default(e),$o.default(e)}function jo(e,t,n){e.helpers[t]&&(e.hooks[t]=e.helpers[t],n||delete e.helpers[t])}});var Wr=_((nt,jr)=>{"use strict";nt.__esModule=!0;var Wo=q();nt.default=function(e){e.registerDecorator("inline",function(t,n,r,i){var s=t;return n.partials||(n.partials={},s=function(o,a){var l=r.partials;r.partials=Wo.extend({},l,n.partials);var u=t(o,a);return r.partials=l,u}),n.partials[i.args[0]]=i.fn,s})};jr.exports=nt.default});var zr=_(Jt=>{"use strict";Jt.__esModule=!0;Jt.registerDefaultDecorators=Yo;function zo(e){return e&&e.__esModule?e:{default:e}}var Go=Wr(),Ko=zo(Go);function Yo(e){Ko.default(e)}});var Xt=_((rt,Gr)=>{"use strict";rt.__esModule=!0;var Jo=q(),fe={methodMap:["debug","info","warn","error"],level:"info",lookupLevel:function(t){if(typeof t=="string"){var n=Jo.indexOf(fe.methodMap,t.toLowerCase());n>=0?t=n:t=parseInt(t,10)}return t},log:function(t){if(t=fe.lookupLevel(t),typeof console<"u"&&fe.lookupLevel(fe.level)<=t){var n=fe.methodMap[t];console[n]||(n="log");for(var r=arguments.length,i=Array(r>1?r-1:0),s=1;s{"use strict";Zt.__esModule=!0;Zt.createNewLookupObject=Zo;var Xo=q();function Zo(){for(var e=arguments.length,t=Array(e),n=0;n{"use strict";xe.__esModule=!0;xe.createProtoAccessControl=ra;xe.resultIsAllowed=ia;xe.resetLoggedProperties=oa;function ea(e){return e&&e.__esModule?e:{default:e}}var Yr=Kr(),ta=Xt(),na=ea(ta),it=Object.create(null);function ra(e){var t=Object.create(null);t.constructor=!1,t.__defineGetter__=!1,t.__defineSetter__=!1,t.__lookupGetter__=!1;var n=Object.create(null);return n.__proto__=!1,{properties:{whitelist:Yr.createNewLookupObject(n,e.allowedProtoProperties),defaultValue:e.allowProtoPropertiesByDefault},methods:{whitelist:Yr.createNewLookupObject(t,e.allowedProtoMethods),defaultValue:e.allowProtoMethodsByDefault}}}function ia(e,t,n){return Jr(typeof e=="function"?t.methods:t.properties,n)}function Jr(e,t){return e.whitelist[t]!==void 0?e.whitelist[t]===!0:e.defaultValue!==void 0?e.defaultValue:(sa(t),!1)}function sa(e){it[e]!==!0&&(it[e]=!0,na.default.log("error",'Handlebars: Access has been denied to resolve the property "'+e+`" because it is not an "own property" of its parent. +You can add a runtime option to disable the check or this warning: +See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details`))}function oa(){Object.keys(it).forEach(function(e){delete it[e]})}});var sn=_(U=>{"use strict";U.__esModule=!0;U.HandlebarsEnvironment=rn;function Xr(e){return e&&e.__esModule?e:{default:e}}var oe=q(),aa=X(),tn=Xr(aa),la=Yt(),ua=zr(),ca=Xt(),st=Xr(ca),da=en(),ha="4.7.8";U.VERSION=ha;var fa=8;U.COMPILER_REVISION=fa;var pa=7;U.LAST_COMPATIBLE_COMPILER_REVISION=pa;var ma={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};U.REVISION_CHANGES=ma;var nn="[object Object]";function rn(e,t,n){this.helpers=e||{},this.partials=t||{},this.decorators=n||{},la.registerDefaultHelpers(this),ua.registerDefaultDecorators(this)}rn.prototype={constructor:rn,logger:st.default,log:st.default.log,registerHelper:function(t,n){if(oe.toString.call(t)===nn){if(n)throw new tn.default("Arg not supported with multiple helpers");oe.extend(this.helpers,t)}else this.helpers[t]=n},unregisterHelper:function(t){delete this.helpers[t]},registerPartial:function(t,n){if(oe.toString.call(t)===nn)oe.extend(this.partials,t);else{if(typeof n>"u")throw new tn.default('Attempting to register a partial called "'+t+'" as undefined');this.partials[t]=n}},unregisterPartial:function(t){delete this.partials[t]},registerDecorator:function(t,n){if(oe.toString.call(t)===nn){if(n)throw new tn.default("Arg not supported with multiple decorators");oe.extend(this.decorators,t)}else this.decorators[t]=n},unregisterDecorator:function(t){delete this.decorators[t]},resetLoggedPropertyAccesses:function(){da.resetLoggedProperties()}};var ga=st.default.log;U.log=ga;U.createFrame=oe.createFrame;U.logger=st.default});var ei=_((ot,Zr)=>{"use strict";ot.__esModule=!0;function on(e){this.string=e}on.prototype.toString=on.prototype.toHTML=function(){return""+this.string};ot.default=on;Zr.exports=ot.default});var ti=_(an=>{"use strict";an.__esModule=!0;an.wrapHelper=va;function va(e,t){if(typeof e!="function")return e;var n=function(){var i=arguments[arguments.length-1];return arguments[arguments.length-1]=t(i),e.apply(this,arguments)};return n}});var oi=_(Z=>{"use strict";Z.__esModule=!0;Z.checkRevision=ka;Z.template=Sa;Z.wrapProgram=at;Z.resolvePartial=_a;Z.invokePartial=La;Z.noop=ii;function ya(e){return e&&e.__esModule?e:{default:e}}function wa(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var ba=q(),z=wa(ba),Ea=X(),G=ya(Ea),K=sn(),ni=Yt(),xa=ti(),ri=en();function ka(e){var t=e&&e[0]||1,n=K.COMPILER_REVISION;if(!(t>=K.LAST_COMPATIBLE_COMPILER_REVISION&&t<=K.COMPILER_REVISION))if(t{"use strict";lt.__esModule=!0;lt.default=function(e){(function(){typeof globalThis!="object"&&(Object.prototype.__defineGetter__("__magic__",function(){return this}),__magic__.globalThis=__magic__,delete Object.prototype.__magic__)})();var t=globalThis.Handlebars;e.noConflict=function(){return globalThis.Handlebars===e&&(globalThis.Handlebars=t),e}};ai.exports=lt.default});var fi=_((ut,hi)=>{"use strict";ut.__esModule=!0;function un(e){return e&&e.__esModule?e:{default:e}}function cn(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var Aa=sn(),ui=cn(Aa),Ia=ei(),Ca=un(Ia),Ra=X(),Ma=un(Ra),Na=q(),ln=cn(Na),Ha=oi(),ci=cn(Ha),Da=li(),Ba=un(Da);function di(){var e=new ui.HandlebarsEnvironment;return ln.extend(e,ui),e.SafeString=Ca.default,e.Exception=Ma.default,e.Utils=ln,e.escapeExpression=ln.escapeExpression,e.VM=ci,e.template=function(t){return ci.template(t,e)},e}var ke=di();ke.create=di;Ba.default(ke);ke.default=ke;ut.default=ke;hi.exports=ut.default});var ae=_((Rc,pi)=>{pi.exports=fi().default});var Ui=_((Vi,$i)=>{(function(){var e=function(t){var n=new e.Builder;return n.pipeline.add(e.trimmer,e.stopWordFilter,e.stemmer),n.searchPipeline.add(e.stemmer),t.call(n,n),n.build()};e.version="2.3.9";e.utils={},e.utils.warn=(function(t){return function(n){t.console&&console.warn&&console.warn(n)}})(this),e.utils.asString=function(t){return t==null?"":t.toString()},e.utils.clone=function(t){if(t==null)return t;for(var n=Object.create(null),r=Object.keys(t),i=0;i0){var c=e.utils.clone(n)||{};c.position=[a,u],c.index=s.length,s.push(new e.Token(r.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/;e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,n){n in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+n),t.label=n,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var n=t.label&&t.label in this.registeredFunctions;n||e.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. +`,t)},e.Pipeline.load=function(t){var n=new e.Pipeline;return t.forEach(function(r){var i=e.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(n){e.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},e.Pipeline.prototype.after=function(t,n){e.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(t);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},e.Pipeline.prototype.before=function(t,n){e.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(t);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},e.Pipeline.prototype.remove=function(t){var n=this._stack.indexOf(t);n!=-1&&this._stack.splice(n,1)},e.Pipeline.prototype.run=function(t){for(var n=this._stack.length,r=0;r1&&(ot&&(r=s),o!=t);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==t||o>t)return s*2;if(ol?c+=2:a==l&&(n+=r[u+1]*i[c+1],u+=2,c+=2);return n},e.Vector.prototype.similarity=function(t){return this.dot(t)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var t=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new e.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}s.str.length==1&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c=s.str.charAt(0),d=s.str.charAt(1),h;d in s.node.edges?h=s.node.edges[d]:(h=new e.TokenSet,s.node.edges[d]=h),s.str.length==1&&(h.final=!0),i.push({node:h,editsRemaining:s.editsRemaining-1,str:c+s.str.slice(2)})}}}return r},e.TokenSet.fromString=function(t){for(var n=new e.TokenSet,r=n,i=0,s=t.length;i=t;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};e.Index=function(t){this.invertedIndex=t.invertedIndex,this.fieldVectors=t.fieldVectors,this.tokenSet=t.tokenSet,this.fields=t.fields,this.pipeline=t.pipeline},e.Index.prototype.search=function(t){return this.query(function(n){var r=new e.QueryParser(t,n);r.parse()})},e.Index.prototype.query=function(t){for(var n=new e.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=t},e.Builder.prototype.k1=function(t){this._k1=t},e.Builder.prototype.add=function(t,n){var r=t[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,n;do t=this.next(),n=t.charCodeAt(0);while(n>47&&n<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var n=t.next();if(n==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){t.escapeCharacter();continue}if(n==":")return e.QueryLexer.lexField;if(n=="~")return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if(n=="^")return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if(n=="+"&&t.width()===1||n=="-"&&t.width()===1)return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(n.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}},e.QueryParser=function(t,n){this.lexer=new e.QueryLexer(t),this.query=n,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var t=this.peekLexeme();return this.lexemeIdx+=1,t},e.QueryParser.prototype.nextClause=function(){var t=this.currentClause;this.query.clause(t),this.currentClause={}},e.QueryParser.parseClause=function(t){var n=t.peekLexeme();if(n!=null)switch(n.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new e.QueryParseError(r,n.start,n.end)}},e.QueryParser.parsePresence=function(t){var n=t.consumeLexeme();if(n!=null){switch(n.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new e.QueryParseError(r,n.start,n.end)}var i=t.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new e.QueryParseError(r,n.start,n.end)}switch(i.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new e.QueryParseError(r,i.start,i.end)}}},e.QueryParser.parseField=function(t){var n=t.consumeLexeme();if(n!=null){if(t.query.allFields.indexOf(n.str)==-1){var r=t.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.fields=[n.str];var s=t.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new e.QueryParseError(i,n.start,n.end)}if(s.type===e.QueryLexer.TERM)return e.QueryParser.parseTerm;var i="expecting term, found '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}},e.QueryParser.parseTerm=function(t){var n=t.consumeLexeme();if(n!=null){t.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var r=t.peekLexeme();if(r==null){t.nextClause();return}switch(r.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new e.QueryParseError(i,r.start,r.end)}}},e.QueryParser.parseEditDistance=function(t){var n=t.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.editDistance=r;var s=t.peekLexeme();if(s==null){t.nextClause();return}switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var n=t.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.boost=r;var s=t.peekLexeme();if(s==null){t.nextClause();return}switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}}},(function(t,n){typeof define=="function"&&define.amd?define(n):typeof Vi=="object"?$i.exports=n():t.lunr=n()})(this,function(){return e})})()});var ie={plain:"plain",function:"function",module:"module"},Is=[{href:"typespecs.html#basic-types",hint:{kind:ie.plain,description:"Basic type"}},{href:"typespecs.html#literals",hint:{kind:ie.plain,description:"Literal"}},{href:"typespecs.html#built-in-types",hint:{kind:ie.plain,description:"Built-in type"}}],Me={cancelHintFetching:null};function Wn(e){if(Gn(e))return!0;let t=/#.*\//;return e.includes("#")&&!t.test(e)?!1:e.includes(".html")}function zn(e){let t=Gn(e);return t?Promise.resolve(t):Cs(e)}function Gn(e){let t=Is.find(n=>e.includes(n.href));return t?t.hint:null}function Cs(e){let t=e.replace(".html",".html?hint=true");return new Promise((n,r)=>{let i=document.createElement("iframe");i.setAttribute("src",t),i.style.display="none";function s(a){let{href:l,hint:u}=a.data;t===l&&(o(),n(u))}Me.cancelHintFetching=()=>{o(),r(new Error("cancelled"))};function o(){i.remove(),window.removeEventListener("message",s),Me.cancelHintFetching=null}window.addEventListener("message",s),document.body.appendChild(i)})}function Kn(){Me.cancelHintFetching&&Me.cancelHintFetching()}function Yn(e){let n=e.querySelector("h1").textContent,r=e.querySelector(".docstring > p"),i=r?r.innerHTML:"";return{kind:ie.function,title:n.trim(),description:i.trim()}}function Jn(e){let n=e.querySelector("h1 > span").textContent,r=e.querySelector("#moduledoc p"),i=r?r.innerHTML:"";return{kind:ie.module,title:n.trim(),description:i.trim()}}var f=document.querySelector.bind(document),A=document.querySelectorAll.bind(document);function Xn(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function He(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function De(){return document.getElementById("main").dataset.type}var Ne=["H1","H2","H3","H4","H5","H6"];function Zn(e=!1){let t=window.location.hash.replace(/^#/,"");if(!t)return e?document.getElementById("top-content"):null;let n=document.getElementById(t);if(!n)return null;if(n.matches(".detail"))return n;if(Ne.includes(n.tagName))return Tt(n);let r=Rs(n);return r||document.getElementById("top-content")}function Rs(e){let t=e.previousElementSibling;for(;t;){if(Ne.includes(t.tagName))return Tt(t);t=t.previousElementSibling}let n=e.parentNode;for(;n;){for(t=n.previousElementSibling;t;){if(Ne.includes(t.tagName))return Tt(t);t=t.previousElementSibling}n=n.parentNode}return null}function Tt(e){let t=document.createElement("div"),n=[e],r=e;for(;(r=r.nextSibling)&&!(Ne.includes(r.tagName)&&r.tagName<=e.tagName);)n.push(r);return t.append(...n),t}function er(e){return new URLSearchParams(window.location.search).get(e)}function Pt(e){return fetch(e).then(t=>t.ok).catch(()=>!1)}function he(e){return!e||e.trim()===""}function tr(e,t){let n;return function(...i){clearTimeout(n),n=setTimeout(()=>{n=null,e(...i)},t)}}function Be(){return document.head.querySelector("meta[name=project][content]").content}function Qe(){return document.documentElement.classList.contains("apple-os")}function E(e,t,n){let r=document.createElement(e);for(let i in t)t[i]!=null&&r.setAttribute(i,t[i]);return n&&r.replaceChildren(...n),r}var Ms=new URLSearchParams(window.location.search),Ns=window.self!==window.parent,Ot=Ns&&Ms.has("hint"),C=Ot;function qe(){return window.sidebarNodes||{}}function nr(){return window.versionNodes||[]}if(Ot&&C){let e=Zn(),t=e?Yn(e):["modules","tasks"].includes(De())?Jn(f(".content-inner")):null;if(t){let n={hint:{...t,version:Be()},href:window.location.href};window.parent.postMessage(n,"*")}f(".content-inner")?.replaceChildren()}var At="ex_doc:settings",rr="dark",It="system",Ct="dark",Rt="light";var Hs={tooltips:!0,theme:null,livebookUrl:null},Mt=class{constructor(){this._subscribers=[],this._settings=Hs,this._loadSettings()}get(){return this._settings}update(t){let n=this._settings;this._settings={...this._settings,...t},this._subscribers.forEach(r=>r(this._settings,n)),this._storeSettings()}getAndSubscribe(t){this._subscribers.push(t),t(this._settings)}_loadSettings(){try{let t=localStorage.getItem(At);if(t){let n=JSON.parse(t);this._settings={...this._settings,...n}}this._loadSettingsLegacy()}catch(t){console.error(`Failed to load settings: ${t}`)}}_storeSettings(){try{this._storeSettingsLegacy(),localStorage.setItem(At,JSON.stringify(this._settings))}catch(t){console.error(`Failed to persist settings: ${t}`)}}_loadSettingsLegacy(){localStorage.getItem("tooltipsDisabled")!==null&&(this._settings={...this._settings,tooltips:!1}),localStorage.getItem("night-mode")==="true"&&(this._settings={...this._settings,nightMode:!0}),this._settings.nightMode===!0&&(this._settings={...this._settings,theme:"dark"})}_storeSettingsLegacy(){this._settings.tooltips?localStorage.removeItem("tooltipsDisabled"):localStorage.setItem("tooltipsDisabled","true"),this._settings.nightMode!==null?localStorage.setItem("night-mode",this._settings.nightMode===!0?"true":"false"):localStorage.removeItem("night-mode"),this._settings.theme!==null?(localStorage.setItem("night-mode",this._settings.theme==="dark"?"true":"false"),this._settings.nightMode=this._settings.theme==="dark"):(delete this._settings.nightMode,localStorage.removeItem("night-mode"))}},R=new Mt;var ir=!1,Fe=null,J=null;function Ve(e){ir||(ir=!0,J=document.getElementById("toast"),J?.addEventListener("click",()=>{clearTimeout(Fe),J.classList.remove("show")})),J&&(clearTimeout(Fe),J.innerText=e,J.classList.add("show"),Fe=setTimeout(()=>{J.classList.remove("show"),Fe=setTimeout(function(){J.innerText=""},1e3)},5e3))}var Nt=[It,Ct,Rt],sr=window.matchMedia("(prefers-color-scheme: dark)");R.getAndSubscribe(or);sr.addEventListener("change",or);function or(){let e=lr(),t=e===Ct||e!==Rt&&sr.matches;document.body.classList.toggle(rr,t)}function ar(){let e=Nt[Nt.indexOf(lr())+1]||Nt[0];R.update({theme:e}),Ve(`Set theme to "${e}"`)}function lr(){return new URLSearchParams(window.location.search).get("theme")||R.get().theme||It}var Er=Y(fr());var Dt="sidebar_state",Bt="closed",pr="open",mr="sidebar_width";var Ue="sidebar-open",je="sidebar-transition";var gr=!1;function vr(){if(gr)return;gr=!0;let e=document.getElementById("sidebar-list-nav");if(!e)return;let t=De(),n={extras:e.dataset.extras,modules:"Modules",tasks:'Mix Tasks'};Object.entries(n).forEach(([r,i])=>{let s=qe()[r];if(!s?.length)return;let o=`${r}-list-tab-button`,a=`${r}-tab-panel`,l=r===t,u=E("button",{id:o,role:"tab",tabindex:l?0:-1,"aria-selected":l||void 0,"aria-controls":a});u.innerHTML=i,u.addEventListener("keydown",no),u.addEventListener("click",ro),e.appendChild(E("li",{},[u]));let c=E("ul",{class:"full-list"});c.addEventListener("click",io);let d=E("div",{id:a,class:"sidebar-tabpanel",role:"tabpanel","aria-labelledby":o,hidden:l?void 0:""},[c]);document.getElementById("sidebar").appendChild(d);let h="",p,g;c.replaceChildren(...s.flatMap(m=>{let v=[],w=Array.isArray(m.headers),x=w?void 0:"no",P=m?.url||`${m.id}.html`;return m.group!==h&&(v.push(E("li",{class:"group",translate:x},[m.group])),h=m.group,p=void 0),m.nested_context&&m.nested_context!==p?(p=m.nested_context,g!==p&&v.push(E("li",{class:"nesting-context",translate:"no","aria-hidden":!0},[p]))):g=m.title,v.push(E("li",{},[E("a",{href:P,translate:x,...m.url?{rel:"nofollow"}:{}},[m.nested_title||m.title,m.url?E("i",{class:"external-link ri-external-link-line"}):null].filter(Boolean)),...qt(`node-${m.id}-headers`,w?eo(m):to(m))])),v}))}),Qt(),requestAnimationFrame(yr),window.addEventListener("hashchange",Qt),window.addEventListener("exdoc:loaded",Qt)}function qt(e,t){return t.length?[E("button",{"aria-label":"expand","aria-expanded":!1,"aria-controls":e}),E("ul",{id:e},t)]:[]}function eo(e){return e.headers.map(({id:t,anchor:n})=>E("li",{},[E("a",{href:`${e.id}.html#${n}`},[t])]))}function to(e){let t=[];return e.sections?.length&&t.push(E("li",{},[E("a",{href:`${e.id}.html#content`},["Sections"]),...qt(`${e.id}-sections-list`,e.sections.map(({id:n,anchor:r})=>E("li",{},[E("a",{href:`${e.id}.html#${r}`},[n])])))])),e.nodeGroups&&(t.push(E("li",{},[E("a",{href:`${e.id}.html#summary`},["Summary"])])),t.push(...e.nodeGroups.map(({key:n,name:r,nodes:i})=>E("li",{},[E("a",{href:`${e.id}.html#${n}`},[r]),...qt(`node-${e.id}-group-${n}-list`,i.map(({anchor:s,title:o,id:a})=>E("li",{},[E("a",{href:`${e.id}.html#${s}`,title:o,translate:"no"},[a])])))])))),t}function Ft(e){let t=document.getElementById("sidebar-list-nav").querySelector("[aria-selected]");t!==e&&(t&&(t.removeAttribute("aria-selected"),t.setAttribute("tabindex","-1"),document.getElementById(t.getAttribute("aria-controls")).setAttribute("hidden","hidden")),e.setAttribute("aria-selected","true"),e.setAttribute("tabindex","0"),document.getElementById(e.getAttribute("aria-controls")).removeAttribute("hidden"))}function yr(){f("#sidebar [role=tabpanel]:not([hidden]) a[aria-selected]")?.scrollIntoView()}function Qt(){let e=document.getElementById("sidebar"),{pathname:t,hash:n}=window.location,r=t.split("/").pop().replace(/\.html$/,"")+".html",i=e.querySelector(`li a[href="${r+n}"]`)||e.querySelector(`li a[href="${r}"]`);if(!i)return;e.querySelectorAll(".full-list a[aria-selected]").forEach(o=>{o.removeAttribute("aria-selected")}),e.querySelectorAll(".full-list button[aria-expanded=true]").forEach(o=>{o.setAttribute("aria-expanded",!1)});let s=i.parentElement;for(;s;){if(s.tagName==="LI"){let o=s.firstChild;o.setAttribute("aria-selected",o.getAttribute("href")===r?"page":"true");let a=o.nextSibling;a?.tagName==="BUTTON"&&a.setAttribute("aria-expanded",!0)}else if(s.role==="tabpanel"){s.hasAttribute("hidden")&&Ft(document.getElementById(s.getAttribute("aria-labelledby")));break}s=s.parentElement}}function no(e){if(!["ArrowRight","ArrowLeft"].includes(e.key))return;let t=Array.from(A('#sidebar-list-nav [role="tab"]')),r=t.indexOf(e.currentTarget)+(e.key==="ArrowRight"?1:-1),i=t.at(r%t.length);Ft(i),i.focus()}function ro(e){Ft(e.currentTarget),yr()}function io(e){let t=e.target;t.tagName==="BUTTON"&&t.setAttribute("aria-expanded",t.getAttribute("aria-expanded")==="false")}var oo=300,xr=".sidebar-toggle",Vt=window.matchMedia(`screen and (max-width: ${768}px)`);if(!C){window.addEventListener("exdoc:loaded",wr);let e=document.getElementById("sidebar"),t=f(xr);t.addEventListener("click",We),document.body.addEventListener("click",i=>{Vt.matches&&$t()&&!e.contains(i.target)&&!t.contains(i.target)&&We()});let n=window.innerWidth;window.addEventListener("resize",(0,Er.default)(()=>{n!==window.innerWidth&&(n=window.innerWidth,wr())},100));let r=new ResizeObserver(([i])=>{if(!i)return;let s=i.contentRect.width;sessionStorage.setItem(mr,s),document.body.style.setProperty("--sidebarWidth",`${s}px`)});e.addEventListener("mousedown",()=>r.observe(e)),e.addEventListener("mouseup",()=>r.unobserve(e)),window.addEventListener("hashchange",ao)}function wr(){let t=sessionStorage.getItem(Dt)!==Bt&&!Vt.matches;Sr(t)}function We(){let e=!$t();return sessionStorage.setItem(Dt,e?pr:Bt),Ut(e)}function $t(){return document.body.classList.contains(Ue)}function kr(){return document.body.classList.contains(Ue)&&!document.body.classList.contains(je)}function Sr(e){e&&vr(),document.body.classList.toggle(Ue,e),f(xr).setAttribute("aria-expanded",e?"true":"false")}var br;function Ut(e){return new Promise(t=>{document.body.classList.add(je),document.body.scrollTop,Sr(e),clearTimeout(br),br=setTimeout(()=>{document.body.classList.remove(je),t()},oo)})}function _r(){return Ut(!0)}function ao(){Vt.matches&&$t()&&Ut(!1)}var mi=Y(ae());var dn=Y(ae());dn.registerHelper("isArray",function(e,t){return Array.isArray(e)?t.fn(this):t.inverse(this)});dn.registerHelper("isNonEmptyArray",function(e,t){return Array.isArray(e)&&e.length>0?t.fn(this):t.inverse(this)});var gi=mi.template({1:function(e,t,n,r,i){var s,o,a=t??(e.nullContext||{}),l=e.hooks.helperMissing,u="function",c=e.escapeExpression,d=e.lookupProperty||function(h,p){if(Object.prototype.hasOwnProperty.call(h,p))return h[p]};return' +`},2:function(e,t,n,r,i){return" selected disabled"},4:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return` +`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`
+ +`+((s=a(n,"if").call(o,t!=null?a(t,"latestVersion"):t,{name:"if",hash:{},fn:e.program(4,i,0),inverse:e.noop,data:i,loc:{start:{line:13,column:2},end:{line:19,column:9}}}))!=null?s:"")+`
+`},useData:!0});var Qa=".sidebar-projectVersion",vi=".sidebar-projectVersion select",qa=".sidebar-staleVersion a";if(!C){let e=nr(),t=f(Qa);if(e.length>0||!t){let n=t.textContent.trim(),i=(e.some(u=>u.version===n)?e:[{version:n,url:"#"},...e]).map(u=>({...u,isCurrentVersion:u.version===n})),s=e.find(u=>u.latest),o=s?.version!==n?s?.url:null;n.includes("-")&&e.findIndex(u=>u.version===n)u.latest)&&(o=null),t.innerHTML=gi({nodes:i,latestVersion:o});let a=f(vi);a.addEventListener("change",Va),Fa(a);let l=f(qa);l&&l.addEventListener("click",$a)}}function Fa(e){let t=document.createElement("span");t.style.visibility="hidden",t.style.position="absolute",t.style.whiteSpace="nowrap",t.style.font=window.getComputedStyle(e).font,t.textContent=e.options[e.selectedIndex].text,document.body.appendChild(t),e.style.width=`${t.offsetWidth+20}px`,document.body.removeChild(t)}function Va(e){let t=e.target.value,n=window.location.pathname.split("/").pop()+window.location.hash,r=`${t}/${n}`;Pt(r).then(i=>{i?window.location.href=r:window.location.href=t})}function $a(e){let t=this.href,n=window.location.pathname.split("/").pop()+window.location.hash,r=`${t}/${n}`;e.preventDefault(),Pt(r).then(i=>{i?window.location.href=r:window.location.href=t})}function hn(){let e=f(vi);e&&(e.focus(),e.addEventListener("keydown",t=>{(t.key==="Escape"||t.key==="v")&&(t.preventDefault(),e.blur())}),navigator.userActivation.isActive&&"showPicker"in HTMLSelectElement.prototype&&e.showPicker())}var Ua="content",ja="tabs-open",Wa="tabs-close",za="H3",Ga="tabset";window.addEventListener("exdoc:loaded",Ka);function Ka(){let e=[],t=[],n=document.createNodeIterator(document.getElementById(Ua),NodeFilter.SHOW_COMMENT,i=>i.nodeValue.trim()===ja?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT),r;for(;r=n.nextNode();){let i=[];e.push([r,i]);let s,o=r;for(;o=o.nextSibling;)if(o.nodeName===za){s=[];let a=o.querySelector(".text")?.childNodes||o.childNodes;i.push([a,s]),t.push(o)}else if(o.nodeName==="#comment"&&o.nodeValue.trim()===Wa){t.push(o);break}else s&&s.push(o)}e.forEach(([i,s],o)=>{let a=E("div",{class:Ga});i.parentNode.replaceChild(a,i);let l=E("div",{role:"tablist",class:"tabset-tablist"});a.appendChild(l),s.forEach(([u,c],d)=>{let h=d===0,p=`tab-${o}-${d}`,g=`tabpanel-${o}-${d}`,m=E("button",{role:"tab",id:p,class:"tabset-tab",tabindex:h?0:-1,"aria-selected":h,"aria-controls":g},u);m.addEventListener("click",Ya),m.addEventListener("keydown",Ja),l.appendChild(m);let v=E("div",{role:"tabpanel",id:g,class:"tabset-panel",hidden:h?void 0:"","aria-labelledby":p},c);a.appendChild(v)})}),t.forEach(i=>{i.parentNode.removeChild(i)})}function Ya(e){wi(e.currentTarget)}function Ja(e){if(yi[e.code]){e.preventDefault();let t=[...e.currentTarget.parentNode.childNodes],n=t.indexOf(e.currentTarget),r=yi[e.code](n,t.length);wi(t.at(r%t.length))}}var yi={ArrowLeft:e=>e-1,ArrowRight:e=>e+1,Home:()=>0,End:(e,t)=>t-1};function wi(e){let t=e.parentNode.querySelector("[aria-selected=true]");if(t===e)return;t.setAttribute("aria-selected","false"),t.tabIndex=-1,e.setAttribute("aria-selected","true"),e.tabIndex=0,e.focus();let n=document.getElementById(t.getAttribute("aria-controls"));n.setAttribute("hidden",""),n.tabIndex=-1;let r=document.getElementById(e.getAttribute("aria-controls"));r.removeAttribute("hidden"),r.tabIndex=0}window.addEventListener("exdoc:loaded",Xa);function Xa(){let e=window.location.pathname.replace(/(\.html)?$/,".livemd"),t=encodeURIComponent(new URL(e,window.location.href).toString());R.getAndSubscribe(({livebookUrl:n})=>{let r=n?`${n}/import?url=${t}`:`https://livebook.dev/run?url=${t}`;for(let i of A(".livebook-badge"))i.href=r})}var Za="hll";window.addEventListener("exdoc:loaded",el);function el(){A("[data-group-id]").forEach(e=>{e.addEventListener("mouseenter",bi),e.addEventListener("mouseleave",bi)})}function bi(e){let t=e.currentTarget,n=e.type==="mouseenter",r=t.getAttribute("data-group-id");t.parentElement.querySelectorAll(`[data-group-id="${r}"]`).forEach(i=>{i.classList.toggle(Za,n)})}var j={module:"module",moduleChild:"module-child",mixTask:"mix-task",extra:"extra",section:"section"},tl=10;function xi(e,t=null){let n=t||window.autocompleteLimit||tl;if(he(e))return[];let r=qe(),i=[...fn(r.modules,e,j.module,"module"),...nl(r.modules,e,j.moduleChild),...fn(r.tasks,e,j.mixTask,"mix task"),...fn(r.extras,e,j.extra,"page"),...pn(r.modules,e,j.section,"module"),...pn(r.tasks,e,j.section,"mix task"),...pn(r.extras,e,j.section,"page")].filter(s=>s!==null);return ul(i).slice(0,n)}function fn(e,t,n,r){return e.map(i=>i.searchData?null:il(i,t,n,r))}function nl(e,t,n){return e.filter(r=>r.nodeGroups).flatMap(r=>r.nodeGroups.flatMap(({key:i,nodes:s})=>{let o=ll(i);return s.map(a=>sl(a,r.id,t,n,o)||al(a,r.id,t,n,o))}))}function pn(e,t,n,r){return e.flatMap(i=>rl(i).map(s=>ol(i,s,t,n,r)))}function rl(e){return e.searchData?e.searchData:(e.sections||[]).concat(e.headers||[])}function il(e,t,n,r){return dt(e.title,t)?{link:`${e.id}.html`,title:pt(e.title,t),description:null,matchQuality:ht(e.title,t),deprecated:e.deprecated,labels:[r],category:n}:null}function sl(e,t,n,r,i){return dt(e.id,n)?{link:`${t}.html#${e.anchor}`,title:pt(e.id,n),labels:[i],description:t,matchQuality:ht(e.id,n),deprecated:e.deprecated,category:r}:null}function ol(e,t,n,r,i){if(!ki(t.id,n))return null;let s;return t.anchor===""?s=`${e.id}.html`:s=`${e.id}.html#${t.anchor}`,{link:s,title:pt(t.id,n),description:e.title,matchQuality:ht(t.id,n),labels:t.labels||[i,"section"],category:r}}function al(e,t,n,r,i){let s=`${t}.${e.id}`,o=`${t}:${e.id}`,a,l;if(dt(s,n))a=s,l=/\./g;else if(dt(o,n))a=o,l=/:/g;else return null;let u=n.replace(l," ");return ki(e.id,u)?{link:`${t}.html#${e.anchor}`,title:pt(e.id,u),label:i,description:t,matchQuality:ht(a,n),deprecated:e.deprecated,category:r}:null}function ll(e){switch(e){case"callbacks":return"callback";case"types":return"type";default:return"function"}}function ul(e){return e.slice().sort((t,n)=>t.matchQuality!==n.matchQuality?n.matchQuality-t.matchQuality:Ei(t.category)-Ei(n.category))}function Ei(e){switch(e){case j.module:return 1;case j.moduleChild:return 2;case j.mixTask:return 3;default:return 4}}function ki(e,t){return ft(t).some(r=>Si(e,r))}function dt(e,t){return ft(t).every(r=>Si(e,r))}function Si(e,t){return e.toLowerCase().includes(t.toLowerCase())}function ht(e,t){let n=ft(t),i=n.map(o=>o.length).reduce((o,a)=>o+a,0)/e.length,s=cl(e,n[0])?1:0;return i+s}function cl(e,t){return e.toLowerCase().startsWith(t.toLowerCase())}function ft(e){return e.trim().split(/\s+/)}function pt(e,t){let n=ft(t).sort((r,i)=>i.length-r.length);return ct(e,n)}function ct(e,t){if(t.length===0)return e;let[n,...r]=t,i=e.match(new RegExp(`(.*)(${Xn(n)})(.*)`,"i"));if(i){let[,s,o,a]=i;return ct(s,t)+""+He(o)+""+ct(a,t)}else return ct(e,r)}var _i=Y(ae());var Li=_i.template({1:function(e,t,n,r,i){return` Autocompletion results within this project +`},3:function(e,t,n,r,i){return` No autocompletion results within this project +`},5:function(e,t,n,r,i){var s,o,a=t??(e.nullContext||{}),l=e.hooks.helperMissing,u="function",c=e.escapeExpression,d=e.lookupProperty||function(h,p){if(Object.prototype.hasOwnProperty.call(h,p))return h[p]};return' + `+((s=(o=(o=d(n,"title")||(t!=null?d(t,"title"):t))!=null?o:l,typeof o===u?o.call(a,{name:"title",hash:{},data:i,loc:{start:{line:19,column:44},end:{line:19,column:55}}}):o))!=null?s:"")+` +`+((s=d(n,"if").call(a,t!=null?d(t,"description"):t,{name:"if",hash:{},fn:e.program(6,i,0),inverse:e.noop,data:i,loc:{start:{line:20,column:8},end:{line:23,column:15}}}))!=null?s:"")+((s=d(n,"if").call(a,t!=null?d(t,"deprecated"):t,{name:"if",hash:{},fn:e.program(8,i,0),inverse:e.noop,data:i,loc:{start:{line:24,column:8},end:{line:26,column:15}}}))!=null?s:"")+((s=d(n,"each").call(a,t!=null?d(t,"labels"):t,{name:"each",hash:{},fn:e.program(10,i,0),inverse:e.noop,data:i,loc:{start:{line:27,column:8},end:{line:29,column:17}}}))!=null?s:"")+` +`},6:function(e,t,n,r,i){var s,o,a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return` + `+((s=(o=(o=a(n,"description")||(t!=null?a(t,"description"):t))!=null?o:e.hooks.helperMissing,typeof o=="function"?o.call(t??(e.nullContext||{}),{name:"description",hash:{},data:i,loc:{start:{line:22,column:49},end:{line:22,column:66}}}):o))!=null?s:"")+` +`},8:function(e,t,n,r,i){return` deprecated +`},10:function(e,t,n,r,i){return' '+e.escapeExpression(e.lambda(t,t))+` +`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`
+
+
+
+ +`+((s=a(n,"if").call(o,(s=t!=null?a(t,"suggestions"):t)!=null?a(s,"length"):s,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:6,column:8},end:{line:10,column:15}}}))!=null?s:"")+` + + Press RETURN for full-text search + +
+
+`+((s=a(n,"each").call(o,t!=null?a(t,"suggestions"):t,{name:"each",hash:{},fn:e.program(5,i,0),inverse:e.noop,data:i,loc:{start:{line:17,column:6},end:{line:31,column:15}}}))!=null?s:"")+`
+
+
+`},useData:!0});var pe=".autocomplete",mn=".autocomplete-suggestions",Ti=".autocomplete-suggestion",W={autocompleteSuggestions:[],selectedIdx:-1};function dl(){f(pe).classList.add("shown")}function gn(){f(pe).classList.remove("shown")}function Pi(){return f(pe).classList.contains("shown")}function vn(e){W.autocompleteSuggestions=xi(e),W.selectedIdx=-1,he(e)?gn():(hl({term:e,suggestions:W.autocompleteSuggestions}),mt(0),dl())}function hl({term:e,suggestions:t}){let n=Li({suggestions:t,term:e}),r=f(pe);r.innerHTML=n}function Oi(){return W.selectedIdx===-1?null:W.autocompleteSuggestions[W.selectedIdx]}function mt(e){fl(pl(e))}function fl(e){W.selectedIdx=e;let t=f(mn),n=f(`${Ti}.selected`),r=f(`${Ti}[data-index="${W.selectedIdx}"]`);n&&n.classList.remove("selected"),r?(r.classList.add("selected"),r.scrollIntoView({block:"nearest"})):t&&(t.scrollTop=0)}function pl(e){let t=W.autocompleteSuggestions.length+1;return(W.selectedIdx+e+1+t)%t-1}var me="form.search-bar input",ml="form.search-bar .search-close-button",gl=".engine-selector",_e=".engine-button",Le=".engine-dropdown",yt=null;C||window.addEventListener("exdoc:loaded",vl);function vl(){yl(),El(),bl()}function yl(){let e=document.querySelectorAll(".engine-option");if(e.length===0){yt="search.html?q=";return}let t=window.location.pathname;if(t.endsWith("/search.html")||t.endsWith("/search")){let n=Array.from(e).find(r=>r.dataset.engineUrl.startsWith("search.html"));if(n){Ri(n);return}}yt=e[0].dataset.engineUrl}function Ri(e){yt=e.dataset.engineUrl;let n=f(_e)?.querySelector(".engine-name");n&&(n.textContent=e.querySelector(".name").textContent),f(Le)?.querySelectorAll(".engine-option").forEach(i=>{i.setAttribute("aria-checked",i===e?"true":"false")})}function Ai(){let e=f(_e),t=f(Le);if(!e||!t)return;e.getAttribute("aria-expanded")==="true"?Se():wl()}function wl(){let e=f(_e),t=f(Le);if(!e||!t)return;e.setAttribute("aria-expanded","true"),t.removeAttribute("hidden"),t.querySelector(".engine-option")?.focus()}function Se(){let e=f(_e),t=f(Le);!e||!t||(e.setAttribute("aria-expanded","false"),t.setAttribute("hidden",""))}function Mi(e){let t=f(me);t.value=e}function Ni(){let e=f(me);document.body.classList.add("search-focused"),e.focus()}function bl(){let e=f(me),t=parseInt(document.querySelector('meta[name="exdoc:autocomplete-limit"]')?.content);t&&(window.autocompleteLimit=t),e.setAttribute("autocomplete-limit",t)}function El(){let e=f(me);if(document.querySelector('meta[name="exdoc:autocomplete"][content="off"]'))return e.addEventListener("keydown",r=>{r.key==="Enter"&&Ii(r)}),!0;e.addEventListener("keydown",r=>{let i=Qe();r.key==="Escape"?(gt(),e.blur()):r.key==="Enter"?Ii(r):r.key==="ArrowUp"||i&&r.ctrlKey&&r.key==="p"?(mt(-1),r.preventDefault()):(r.key==="ArrowDown"||i&&r.ctrlKey&&r.key==="n")&&(mt(1),r.preventDefault())}),e.addEventListener("input",r=>{vn(r.target.value)}),e.addEventListener("focus",r=>{document.body.classList.contains("search-focused")||(document.body.classList.add("search-focused"),vn(r.target.value))}),e.addEventListener("blur",r=>{let i=r.relatedTarget,s=f(mn);if(i&&s&&s.contains(i))return setTimeout(()=>{Pi()&&e.focus()},1e3),null;vt()}),f(pe).addEventListener("click",r=>{r.shiftKey||r.ctrlKey?e.focus():(gt(),vt())}),f(ml).addEventListener("click",r=>{gt(),vt(),e.blur()});let t=f(_e);t&&(t.addEventListener("click",r=>{r.stopPropagation(),Ai()}),t.addEventListener("keydown",r=>{r.key==="Enter"||r.key===" "?(r.preventDefault(),Ai()):r.key==="Escape"&&Se()})),document.addEventListener("click",r=>{let i=f(gl);i&&!i.contains(r.target)&&Se()});let n=f(Le);n&&(n.querySelectorAll(".engine-option").forEach(r=>{r.addEventListener("click",()=>{Ri(r),Se()})}),n.addEventListener("keydown",r=>{let i=Array.from(n.querySelectorAll(".engine-option")),s=i.indexOf(document.activeElement);if(r.key==="ArrowDown"){r.preventDefault();let o=(s+1)%i.length;i[o]?.focus()}else if(r.key==="ArrowUp"){r.preventDefault();let o=(s-1+i.length)%i.length;i[o]?.focus()}else r.key==="Escape"?(r.preventDefault(),Se(),t?.focus()):(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),document.activeElement?.click())}))}function Ii(e){let t=f(me),n=e.shiftKey||e.ctrlKey,r=Oi();e.preventDefault();let i=n?"_blank":"_self",s=document.createElement("a");s.setAttribute("target",i),r?s.setAttribute("href",r.link):s.setAttribute("href",`${yt}${encodeURIComponent(t.value)}`),s.click(),n||(gt(),vt())}function gt(){let e=f(me);e.value=""}function vt(){document.body.classList.remove("search-focused"),gn()}var yn,Ci=2;window.addEventListener("scroll",function(){let e=window.scrollY;if(yn!==void 0){let t=e-yn;e===0||t>Ci?document.body.classList.remove("scroll-sticky"):e>0&&-t>Ci&&document.body.classList.add("scroll-sticky")}yn=Math.max(0,e)},!1);var Hi=Y(ae());var Di=Hi.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
+ `+e.escapeExpression(e.lambda((s=t!=null?o(t,"hint"):t)!=null?o(s,"description"):s,t))+` +
+`},3:function(e,t,n,r,i){var s,o=e.lambda,a=e.escapeExpression,l=e.lookupProperty||function(u,c){if(Object.prototype.hasOwnProperty.call(u,c))return u[c]};return`
+

+ `+a(o((s=t!=null?l(t,"hint"):t)!=null?l(s,"title"):s,t))+` +
`+a(o((s=t!=null?l(t,"hint"):t)!=null?l(s,"version"):s,t))+`
+

+
+`+((s=l(n,"if").call(t??(e.nullContext||{}),(s=t!=null?l(t,"hint"):t)!=null?l(s,"description"):s,{name:"if",hash:{},fn:e.program(4,i,0),inverse:e.noop,data:i,loc:{start:{line:12,column:2},end:{line:16,column:9}}}))!=null?s:"")},4:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
+ `+((s=e.lambda((s=t!=null?o(t,"hint"):t)!=null?o(s,"description"):s,t))!=null?s:"")+` +
+`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"isPlain"):t,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:1,column:0},end:{line:17,column:7}}}))!=null?s:""},useData:!0});var xl='
',kl='.content a:not([data-no-tooltip=""])',wn=".tooltip",Bi=".tooltip .tooltip-body",Qi="body .content-inner",Sl="#content",qi="tooltip-shown",Te=10,_l=Te*4,Ll=768,Tl=450,Pl=100,le={currentLinkElement:null,hoverDelayTimeout:null};window.addEventListener("exdoc:loaded",Ol);function Ol(){A(kl).forEach(e=>{Al(e)&&(e.addEventListener("mouseenter",Cl),e.addEventListener("mouseleave",Ml))})}function Al(e){return!(Il(e.href)||!Wn(e.href))}function Il(e){let t=e.replace(Sl,"");return window.location.href.split("#")[0]===t}function Cl(e){if(window.innerWidth{zn(t.href).then(Rl).catch(()=>{})},Pl)}function Rl(e){let t=Di({isPlain:e.kind===ie.plain,hint:e}),n=f(Bi);n||(f(Qi).insertAdjacentHTML("beforeend",xl),n=f(Bi)),n.innerHTML=t,Nl(),f(wn).classList.add(qi)}function Ml(){le.currentLinkElement&&(clearTimeout(le.hoverDelayTimeout),Kn(),le.currentLinkElement=null,f(wn)?.classList.remove(qi))}function Nl(){if(!le.currentLinkElement)return;let e=f(wn),t=le.currentLinkElement.getBoundingClientRect(),n=f(Qi).getBoundingClientRect(),r=e.getBoundingClientRect(),i=Hl(t,n);if(t.left+r.width+Te code:first-child):not(:has(.copy-button))").forEach(e=>{if(!bn){let r=document.createElement("div");r.innerHTML=Fi,bn=r.firstChild}let t=bn.cloneNode(!0);e.appendChild(t);let n;t.addEventListener("click",()=>{clearTimeout(n);let r=Array.from(e.querySelectorAll("code > *")).filter(i=>!i.classList.contains("unselectable")||i.textContent!=="$ ").map(i=>i.textContent).join("");navigator.clipboard.writeText(r),t.classList.add("clicked"),t.disabled=!0,n=setTimeout(()=>{t.classList.remove("clicked"),t.disabled=!1},3e3)})})}window.addEventListener("exdoc:loaded",Bl);function Bl(){"clipboard"in navigator&&A("a.copy-markdown").forEach(e=>{e.addEventListener("click",Ql)})}function Ql(e){if(e.ctrlKey||e.metaKey)return;e.preventDefault();let n=e.currentTarget.href,r=new ClipboardItem({"text/plain":fetch(n).then(i=>{if(!i.ok)throw new Error("Failed to fetch markdown");return i.text()}).then(i=>new Blob([i],{type:"text/plain"}))});navigator.clipboard.write([r]).then(()=>{Ve("Page copied as Markdown to clipboard")}).catch(i=>{console.log("Copying Markdown failed:",i),window.confirm("Could not copy to clipboard. Do you want to open the Markdown page instead?")&&(window.location.href=n)})}var F=Y(Ui());var ji=Y(ae());var Wi=ji.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return" Search results for "+e.escapeExpression((s=(s=o(n,"value")||(t!=null?o(t,"value"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"value",hash:{},data:i,loc:{start:{line:3,column:27},end:{line:3,column:36}}}):s))+` +`},3:function(e,t,n,r,i){return` Invalid search +`},5:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"each").call(t??(e.nullContext||{}),t!=null?o(t,"results"):t,{name:"each",hash:{},fn:e.program(6,i,0),inverse:e.noop,data:i,loc:{start:{line:10,column:2},end:{line:21,column:11}}}))!=null?s:""},6:function(e,t,n,r,i){var s,o=e.lambda,a=e.escapeExpression,l=e.lookupProperty||function(u,c){if(Object.prototype.hasOwnProperty.call(u,c))return u[c]};return`
+

+ + `+a(o(t!=null?l(t,"title"):t,t))+" ("+a(o(t!=null?l(t,"type"):t,t))+`) + +

+`+((s=l(n,"each").call(t??(e.nullContext||{}),t!=null?l(t,"excerpts"):t,{name:"each",hash:{},fn:e.program(7,i,0),inverse:e.noop,data:i,loc:{start:{line:17,column:8},end:{line:19,column:17}}}))!=null?s:"")+`
+`},7:function(e,t,n,r,i){var s;return'

'+((s=e.lambda(t,t))!=null?s:"")+`

+`},9:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return((s=(o(n,"isArray")||t&&o(t,"isArray")||e.hooks.helperMissing).call(t??(e.nullContext||{}),t!=null?o(t,"results"):t,{name:"isArray",hash:{},fn:e.program(10,i,0),inverse:e.program(12,i,0),data:i,loc:{start:{line:23,column:2},end:{line:29,column:14}}}))!=null?s:"")+` +

The search functionality is full-text based. Here are some tips:

+ +
    +
  • Multiple words (such as foo bar) are searched as OR
  • +
  • Use * anywhere (such as fo*) as wildcard
  • +
  • Use + before a word (such as +foo) to make its presence required
  • +
  • Use - before a word (such as -foo) to make its absence required
  • +
  • Use : to search on a particular field (such as field:word). The available fields are title, doc and type
  • +
  • Use WORD^NUMBER (such as foo^2) to boost the given word
  • +
  • Use WORD~NUMBER (such as foo~2) to do a search with edit distance on word
  • +
+ +

To quickly go to a module, type, or function, use the autocompletion feature in the sidebar search.

+`},10:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return"

Sorry, we couldn't find anything for "+e.escapeExpression((s=(s=o(n,"value")||(t!=null?o(t,"value"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"value",hash:{},data:i,loc:{start:{line:24,column:48},end:{line:24,column:57}}}):s))+`.

+`},12:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"value"):t,{name:"if",hash:{},fn:e.program(13,i,0),inverse:e.program(15,i,0),data:i,loc:{start:{line:25,column:2},end:{line:29,column:2}}}))!=null?s:""},13:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return"

Invalid search: "+e.escapeExpression((s=(s=o(n,"errorMessage")||(t!=null?o(t,"errorMessage"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"errorMessage",hash:{},data:i,loc:{start:{line:26,column:23},end:{line:26,column:39}}}):s))+`.

+`},15:function(e,t,n,r,i){return`

Please type something into the search bar to perform a search.

+ `},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`

+`+((s=a(n,"if").call(o,t!=null?a(t,"value"):t,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:2,column:2},end:{line:6,column:9}}}))!=null?s:"")+`

+ +`+((s=(a(n,"isNonEmptyArray")||t&&a(t,"isNonEmptyArray")||e.hooks.helperMissing).call(o,t!=null?a(t,"results"):t,{name:"isNonEmptyArray",hash:{},fn:e.program(5,i,0),inverse:e.program(9,i,0),data:i,loc:{start:{line:9,column:0},end:{line:44,column:20}}}))!=null?s:"")},useData:!0});var ql="#search";F.default.tokenizer.separator=/\s+/;F.default.QueryLexer.termSeparator=/\s+/;F.default.Pipeline.registerFunction(Ki,"docTokenSplitter");F.default.Pipeline.registerFunction(Yi,"docTrimmer");window.addEventListener("exdoc:loaded",Fl);function Fl(){let e=window.location.pathname;if(e.endsWith("/search.html")||e.endsWith("/search")){let t=er("q");Vl(t)}}async function Vl(e){if(he(e))En({value:e});else{Mi(e);let t=await $l();try{let n=e.replaceAll(/(\B|\\):/g,"\\:"),r=Zl(t.search(n));En({value:e,results:r})}catch(n){En({value:e,errorMessage:n.message})}}}function En({value:e,results:t,errorMessage:n}){let r=f(ql),i=Wi({value:e,results:t,errorMessage:n});r.innerHTML=i}async function $l(){let e=await Ul();if(e)return e;let t=Yl();return jl(t),t}async function Ul(){try{let e=sessionStorage.getItem(Gi());if(e){let t=await zl(e);return F.default.Index.load(t)}else return null}catch(e){return console.error("Failed to load index: ",e),null}}async function jl(e){try{let t=await Wl(e);sessionStorage.setItem(Gi(),t)}catch(t){console.error("Failed to save index: ",t)}}async function Wl(e){let t=new Blob([JSON.stringify(e)],{type:"application/json"}).stream().pipeThrough(new window.CompressionStream("gzip")),r=await(await new Response(t).blob()).arrayBuffer();return Gl(r)}async function zl(e){let t=new Blob([Kl(e)],{type:"application/json"}).stream().pipeThrough(new window.DecompressionStream("gzip")),n=await new Response(t).text();return JSON.parse(n)}function Gl(e){let t="",n=new Uint8Array(e),r=n.byteLength;for(let i=0;i{this.add(e)})})}function Jl(e){e.pipeline.before(F.default.stemmer,Ki)}function Ki(e){let t=[e],n=/\/\d+$/,r=/\:|\./,i=e.toString();if(i.replace(/^[.,;?!]+|[.,;]+$/g,""),i.startsWith("`")&&i.endsWith("`")&&(i=i.slice(1,-1)),n.test(i)){let o=e.toString().replace(n,"");t.push(e.clone().update(()=>o));let a=o.split(r);if(a.length>1){for(let u of a)t.push(e.clone().update(()=>u));let l=e.toString().split(r);t.push(e.clone().update(()=>l[l.length-1]))}i=a[a.length-1]}else i.startsWith("@")?(i=i.substring(1),t.push(e.clone().update(()=>i))):i.startsWith(":")&&(i=i.substring(1),t.push(e.clone().update(()=>i)));let s=i.split(/\_|\-/);if(s.length>1)for(let o of s)t.push(e.clone().update(()=>o));return t}function Xl(e){e.pipeline.before(F.default.stemmer,Yi)}function Yi(e){return e.update(function(t){return t.replace(/^[^@:\w]+/,"").replace(/[^\?\!\w]+$/,"")})}function Zl(e){return e.filter(t=>zi(t.ref)).map(t=>{let n=zi(t.ref),r=t.matchData.metadata;return{...n,metadata:r,excerpts:eu(n,r)}})}function zi(e){return searchData.items.find(t=>t.ref===e)||null}function eu(e,t){let{doc:n}=e,r=Object.keys(t);return[tu(n,r)]}function tu(e,t){let r=e.split(`\r +\r +`).flatMap(a=>a.split(` + +`)),i;r.length>=2?r[0].trim().startsWith("#")?i=r[1]:i=r[0]:r.length===1?i=r[0]:i=e;let s;i.length>200?s=i.slice(0,200)+"...":s=i;let o=He(s);for(let a of t){if(a.trim()==="")continue;let l=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),u=new RegExp(l,"gi");o=o.replace(u,c=>`${c}`)}return o}var Ji='';var nu='button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',M=null,Sn=null,xn=null,kn=!1;function ru(){M||(document.body.insertAdjacentHTML("beforeend",Ji),M=f(".modal"),M.addEventListener("keydown",e=>{e.key==="Escape"&&ue()}),M.querySelector(".modal-close").addEventListener("click",ue),M.addEventListener("click",e=>{e.target===M&&ue()}))}function Xi(e){if(!kn)if(M.contains(e.target))xn=e.target;else{kn=!0;let t=M.querySelectorAll(nu);xn===t[0]?t[t.length-1].focus():t[0].focus(),kn=!1,xn=document.activeElement}}function wt({title:e,body:t}){ru(),Sn=document.activeElement,document.addEventListener("focus",Xi,!0),M.querySelector(".modal-title").innerHTML=e,M.querySelector(".modal-body").innerHTML=t,M.classList.add("shown"),M.focus()}function ue(){M?.classList.remove("shown"),document.removeEventListener("focus",Xi,!0),Sn?.focus(),Sn=null}function Zi(){return!!M?.classList.contains("shown")}var es='
';var iu="https://hexdocs.pm/%%",su="https://www.erlang.org/doc/apps/%%",ou="https://hex.pm/api/packages?search=name:%%*",au=".display-quick-switch",ns="#quick-switch-input",rs="#quick-switch-results",lu=300,uu=9,is=["erts","asn1","common_test","compiler","crypto","debugger","dialyzer","diameter","edoc","eldap","erl_interface","et","eunit","ftp","inets","jinterface","kernel","megaco","mnesia","observer","odbc","os_mon","parsetools","public_key","reltool","runtime_tools","sasl","snmp","ssh","ssl","stdlib","syntax_tools","tftp","tools","wx","xmerl"],cu=["elixir","eex","ex_unit","hex","iex","logger","mix"].concat(is).map(e=>({name:e})),ss=2,D={autocompleteResults:[],selectedIdx:null};C||window.addEventListener("exdoc:loaded",du);function du(){A(au).forEach(e=>{e.addEventListener("click",Ln)})}function hu(e){if(e.key==="Enter"){let t=e.target.value;pu(t),e.preventDefault()}else e.key==="ArrowUp"?(ts(-1),e.preventDefault()):e.key==="ArrowDown"&&(ts(1),e.preventDefault())}function fu(e){let t=e.target.value;if(t.lengthn.json()).then(n=>{Array.isArray(n)&&(D.autocompleteResults=yu(e,n),D.selectedIdx=null,f(ns).value.length>=ss&&vu(D.autocompleteResults))})}function vu(e){f(rs).replaceChildren(...e.map(({name:t},n)=>{let r=E("div",{class:"quick-switch-result","data-index":n},[t]);return r.addEventListener("click",()=>_n(t)),r}))}function yu(e,t){return cu.concat(t).filter(n=>n.name.toLowerCase().includes(e.toLowerCase())).filter(n=>n.releases===void 0||n.releases[0].has_docs===!0).slice(0,uu)}function ts(e){D.selectedIdx=wu(e);let t=f(".quick-switch-result.selected"),n=f(`.quick-switch-result[data-index="${D.selectedIdx}"]`);t&&t.classList.remove("selected"),n&&n.classList.add("selected")}function wu(e){let t=D.autocompleteResults.length;if(D.selectedIdx===null){if(e>=0)return 0;if(e<0)return t-1}return(D.selectedIdx+e+t)%t}var bu="#settings-modal-content",On=[{key:"c",description:"Toggle sidebar",action:We},{key:"n",description:"Cycle themes",action:ar},{key:"s",description:"Focus search bar",displayAs:"/ or s",action:Tn},{key:"/",action:Tn},{key:"k",hasModifier:!0,action:Tn},{key:"v",description:"Open/focus version select",action:ku},{key:"g",description:"Go to package docs",displayAs:"g",action:Ln},{key:"?",displayAs:"?",description:"Bring up this modal",action:Su}],Pn={shortcutBeingPressed:null};C||(document.addEventListener("keydown",Eu),document.addEventListener("keyup",xu));function Eu(e){if(Pn.shortcutBeingPressed||e.target.matches("input, select, textarea"))return;let t=On.find(n=>n.hasModifier?Qe()&&e.metaKey||e.ctrlKey?n.key===e.key:!1:e.ctrlKey||e.metaKey||e.altKey?!1:n.key===e.key);t&&(Pn.shortcutBeingPressed=t,e.preventDefault(),t.action(e))}function xu(e){Pn.shortcutBeingPressed=null}function Tn(e){ue(),Ni()}function ku(){ue(),kr()?hn():_r().then(hn)}function Su(){_u()?ue():An()}function _u(){return Zi()&&f(bu)}var os=Y(ae());var as=os.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"description"):t,{name:"if",hash:{},fn:e.program(2,i,0),inverse:e.noop,data:i,loc:{start:{line:40,column:6},end:{line:53,column:13}}}))!=null?s:""},2:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
+
+ `+e.escapeExpression(e.lambda(t!=null?o(t,"description"):t,t))+` +
+
+`+((s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"displayAs"):t,{name:"if",hash:{},fn:e.program(3,i,0),inverse:e.program(5,i,0),data:i,loc:{start:{line:46,column:12},end:{line:50,column:19}}}))!=null?s:"")+`
+
+`},3:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return" "+((s=e.lambda(t!=null?o(t,"displayAs"):t,t))!=null?s:"")+` +`},5:function(e,t,n,r,i){var s=e.lookupProperty||function(o,a){if(Object.prototype.hasOwnProperty.call(o,a))return o[a]};return" "+e.escapeExpression(e.lambda(t!=null?s(t,"key"):t,t))+` +`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
+
+ + + + +
+ +
+`},useData:!0});var Lu=".display-settings",Tu="#settings-modal-content",In="#modal-settings-tab",Cn="#modal-keyboard-shortcuts-tab",us="#settings-content",cs="#keyboard-shortcuts-content",Pu=[{title:"Settings",id:"modal-settings-tab"},{title:"Keyboard shortcuts",id:"modal-keyboard-shortcuts-tab"}];window.addEventListener("exdoc:loaded",Ou);function Ou(){A(Lu).forEach(e=>{e.addEventListener("click",An)})}function ls(){f(Cn).classList.remove("active"),f(In).classList.add("active"),f(us).classList.remove("hidden"),f(cs).classList.add("hidden")}function Au(){f(Cn).classList.add("active"),f(In).classList.remove("active"),f(cs).classList.remove("hidden"),f(us).classList.add("hidden")}function An(){wt({title:Pu.map(({id:s,title:o})=>``).join(""),body:as({shortcuts:On})});let e=f(Tu),t=e.querySelector('[name="theme"]'),n=e.querySelector('[name="tooltips"]'),r=e.querySelector('[name="direct_livebook_url"]'),i=e.querySelector('[name="livebook_url"]');R.getAndSubscribe(s=>{t.value=s.theme||"system",n.checked=s.tooltips,s.livebookUrl===null?(r.checked=!1,i.classList.add("hidden"),i.tabIndex=-1):(r.checked=!0,i.classList.remove("hidden"),i.tabIndex=0,i.value=s.livebookUrl)}),t.addEventListener("change",s=>{R.update({theme:s.target.value})}),n.addEventListener("change",s=>{R.update({tooltips:s.target.checked})}),r.addEventListener("change",s=>{let o=s.target.checked?i.value:null;R.update({livebookUrl:o})}),i.addEventListener("input",s=>{R.update({livebookUrl:s.target.value})}),f(In).addEventListener("click",s=>{ls()}),f(Cn).addEventListener("click",s=>{Au()}),ls()}var Rn=new WeakMap;function Mn(e,t,n,r){if(!e&&!Rn.has(t))return!1;let i=Rn.get(t)??new WeakMap;Rn.set(t,i);let s=i.get(n)??new Set;i.set(n,s);let o=s.has(r);return e?s.add(r):s.delete(r),o&&e}function Iu(e,t){let n=e.target;if(n instanceof Text&&(n=n.parentElement),n instanceof Element&&e.currentTarget instanceof Node){let r=n.closest(t);if(r&&e.currentTarget.contains(r))return r}}function Cu(e,t,n,r={}){let{signal:i,base:s=document}=r;if(i?.aborted)return;let{once:o,...a}=r,l=s instanceof Document?s.documentElement:s,u=!!(typeof r=="object"?r.capture:r),c=p=>{let g=Iu(p,String(e));if(g){let m=Object.assign(p,{delegateTarget:g});n.call(l,m),o&&(l.removeEventListener(t,c,a),Mn(!1,l,n,d))}},d=JSON.stringify({selector:e,type:t,capture:u});Mn(!0,l,n,d)||l.addEventListener(t,c,a),i?.addEventListener("abort",()=>{Mn(!1,l,n,d)})}var bt=Cu;function O(){return O=Object.assign?Object.assign.bind():function(e){for(var t=1;tString(e).toLowerCase().replace(/[\s/_.]+/g,"-").replace(/[^\w-]+/g,"").replace(/--+/g,"-").replace(/^-+|-+$/g,"")||t||"",Oe=({hash:e}={})=>window.location.pathname+window.location.search+(e?window.location.hash:""),Ru=(e,t={})=>{let n=O({url:e=e||Oe({hash:!0}),random:Math.random(),source:"swup"},t);window.history.pushState(n,"",e)},Pe=(e=null,t={})=>{e=e||Oe({hash:!0});let n=O({},window.history.state||{},{url:e,random:Math.random(),source:"swup"},t);window.history.replaceState(n,"",e)},Mu=(e,t,n,r)=>{let i=new AbortController;return r=O({},r,{signal:i.signal}),bt(e,t,n,r),{destroy:()=>i.abort()}},N=class e extends URL{constructor(t,n=document.baseURI){super(t.toString(),n),Object.setPrototypeOf(this,e.prototype)}get url(){return this.pathname+this.search}static fromElement(t){let n=t.getAttribute("href")||t.getAttribute("xlink:href")||"";return new e(n)}static fromUrl(t){return new e(t)}};var ge=class extends Error{constructor(t,n){super(t),this.url=void 0,this.status=void 0,this.aborted=void 0,this.timedOut=void 0,this.name="FetchError",this.url=n.url,this.status=n.status,this.aborted=n.aborted||!1,this.timedOut=n.timedOut||!1}};async function Nu(e,t={}){var n;e=N.fromUrl(e).url;let{visit:r=this.visit}=t,i=O({},this.options.requestHeaders,t.headers),s=(n=t.timeout)!=null?n:this.options.timeout,o=new AbortController,{signal:a}=o;t=O({},t,{headers:i,signal:a});let l,u=!1,c=null;s&&s>0&&(c=setTimeout(()=>{u=!0,o.abort("timeout")},s));try{l=await this.hooks.call("fetch:request",r,{url:e,options:t},(v,{url:w,options:x})=>fetch(w,x)),c&&clearTimeout(c)}catch(v){throw u?(this.hooks.call("fetch:timeout",r,{url:e}),new ge(`Request timed out: ${e}`,{url:e,timedOut:u})):v?.name==="AbortError"||a.aborted?new ge(`Request aborted: ${e}`,{url:e,aborted:!0}):v}let{status:d,url:h}=l,p=await l.text();if(d===500)throw this.hooks.call("fetch:error",r,{status:d,response:l,url:h}),new ge(`Server error: ${h}`,{status:d,url:h});if(!p)throw new ge(`Empty response: ${h}`,{status:d,url:h});let{url:g}=N.fromUrl(h),m={url:g,html:p};return!r.cache.write||t.method&&t.method!=="GET"||e!==g||this.cache.set(m.url,m),m}var Hn=class{constructor(t){this.swup=void 0,this.pages=new Map,this.swup=t}get size(){return this.pages.size}get all(){let t=new Map;return this.pages.forEach((n,r)=>{t.set(r,O({},n))}),t}has(t){return this.pages.has(this.resolve(t))}get(t){let n=this.pages.get(this.resolve(t));return n&&O({},n)}set(t,n){n=O({},n,{url:t=this.resolve(t)}),this.pages.set(t,n),this.swup.hooks.callSync("cache:set",void 0,{page:n})}update(t,n){t=this.resolve(t);let r=O({},this.get(t),n,{url:t});this.pages.set(t,r)}delete(t){this.pages.delete(this.resolve(t))}clear(){this.pages.clear(),this.swup.hooks.callSync("cache:clear",void 0,void 0)}prune(t){this.pages.forEach((n,r)=>{t(r,n)&&this.delete(r)})}resolve(t){let{url:n}=N.fromUrl(t);return this.swup.resolveUrl(n)}},Dn=(e,t=document)=>t.querySelector(e),qn=(e,t=document)=>Array.from(t.querySelectorAll(e)),ms=()=>new Promise(e=>{requestAnimationFrame(()=>{requestAnimationFrame(()=>{e()})})});function gs(e){return!!e&&(typeof e=="object"||typeof e=="function")&&typeof e.then=="function"}function Hu(e,t=[]){return new Promise((n,r)=>{let i=e(...t);gs(i)?i.then(n,r):n(i)})}function ds(e,t){let n=e?.closest(`[${t}]`);return n!=null&&n.hasAttribute(t)?n?.getAttribute(t)||!0:void 0}var Bn=class{constructor(t){this.swup=void 0,this.swupClasses=["to-","is-changing","is-rendering","is-popstate","is-animating","is-leaving"],this.swup=t}get selectors(){let{scope:t}=this.swup.visit.animation;return t==="containers"?this.swup.visit.containers:t==="html"?["html"]:Array.isArray(t)?t:[]}get selector(){return this.selectors.join(",")}get targets(){return this.selector.trim()?qn(this.selector):[]}add(...t){this.targets.forEach(n=>n.classList.add(...t))}remove(...t){this.targets.forEach(n=>n.classList.remove(...t))}clear(){this.targets.forEach(t=>{let n=t.className.split(" ").filter(r=>this.isSwupClass(r));t.classList.remove(...n)})}isSwupClass(t){return this.swupClasses.some(n=>t.startsWith(n))}},kt=class{constructor(t,n){this.id=void 0,this.state=void 0,this.from=void 0,this.to=void 0,this.containers=void 0,this.animation=void 0,this.trigger=void 0,this.cache=void 0,this.history=void 0,this.scroll=void 0,this.meta=void 0;let{to:r,from:i,hash:s,el:o,event:a}=n;this.id=Math.random(),this.state=1,this.from={url:i??t.location.url,hash:t.location.hash},this.to={url:r,hash:s},this.containers=t.options.containers,this.animation={animate:!0,wait:!1,name:void 0,native:t.options.native,scope:t.options.animationScope,selector:t.options.animationSelector},this.trigger={el:o,event:a},this.cache={read:t.options.cache,write:t.options.cache},this.history={action:"push",popstate:!1,direction:void 0},this.scroll={reset:!0,target:void 0},this.meta={}}advance(t){this.state=7}};function Du(e){return new kt(this,e)}var Qn=class{constructor(t){this.swup=void 0,this.registry=new Map,this.hooks=["animation:out:start","animation:out:await","animation:out:end","animation:in:start","animation:in:await","animation:in:end","animation:skip","cache:clear","cache:set","content:replace","content:scroll","enable","disable","fetch:request","fetch:error","fetch:timeout","history:popstate","link:click","link:self","link:anchor","link:newtab","page:load","page:view","scroll:top","scroll:anchor","visit:start","visit:transition","visit:abort","visit:end"],this.swup=t,this.init()}init(){this.hooks.forEach(t=>this.create(t))}create(t){this.registry.has(t)||this.registry.set(t,new Map)}exists(t){return this.registry.has(t)}get(t){let n=this.registry.get(t);if(n)return n;console.error(`Unknown hook '${t}'`)}clear(){this.registry.forEach(t=>t.clear())}on(t,n,r={}){let i=this.get(t);if(!i)return console.warn(`Hook '${t}' not found.`),()=>{};let s=O({},r,{id:i.size+1,hook:t,handler:n});return i.set(n,s),()=>this.off(t,n)}before(t,n,r={}){return this.on(t,n,O({},r,{before:!0}))}replace(t,n,r={}){return this.on(t,n,O({},r,{replace:!0}))}once(t,n,r={}){return this.on(t,n,O({},r,{once:!0}))}off(t,n){let r=this.get(t);r&&n?r.delete(n)||console.warn(`Handler for hook '${t}' not found.`):r&&r.clear()}async call(t,n,r,i){let[s,o,a]=this.parseCallArgs(t,n,r,i),{before:l,handler:u,after:c}=this.getHandlers(t,a);await this.run(l,s,o);let[d]=await this.run(u,s,o,!0);return await this.run(c,s,o),this.dispatchDomEvent(t,s,o),d}callSync(t,n,r,i){let[s,o,a]=this.parseCallArgs(t,n,r,i),{before:l,handler:u,after:c}=this.getHandlers(t,a);this.runSync(l,s,o);let[d]=this.runSync(u,s,o,!0);return this.runSync(c,s,o),this.dispatchDomEvent(t,s,o),d}parseCallArgs(t,n,r,i){return n instanceof kt||typeof n!="object"&&typeof r!="function"?[n,r,i]:[void 0,n,r]}async run(t,n=this.swup.visit,r,i=!1){let s=[];for(let{hook:o,handler:a,defaultHandler:l,once:u}of t)if(n==null||!n.done){u&&this.off(o,a);try{let c=await Hu(a,[n,r,l]);s.push(c)}catch(c){if(i)throw c;console.error(`Error in hook '${o}':`,c)}}return s}runSync(t,n=this.swup.visit,r,i=!1){let s=[];for(let{hook:o,handler:a,defaultHandler:l,once:u}of t)if(n==null||!n.done){u&&this.off(o,a);try{let c=a(n,r,l);s.push(c),gs(c)&&console.warn(`Swup will not await Promises in handler for synchronous hook '${o}'.`)}catch(c){if(i)throw c;console.error(`Error in hook '${o}':`,c)}}return s}getHandlers(t,n){let r=this.get(t);if(!r)return{found:!1,before:[],handler:[],after:[],replaced:!1};let i=Array.from(r.values()),s=this.sortRegistrations,o=i.filter(({before:d,replace:h})=>d&&!h).sort(s),a=i.filter(({replace:d})=>d).filter(d=>!0).sort(s),l=i.filter(({before:d,replace:h})=>!d&&!h).sort(s),u=a.length>0,c=[];if(n&&(c=[{id:0,hook:t,handler:n}],u)){let d=a.length-1,{handler:h,once:p}=a[d],g=m=>{let v=a[m-1];return v?(w,x)=>v.handler(w,x,g(m-1)):n};c=[{id:0,hook:t,once:p,handler:h,defaultHandler:g(d)}]}return{found:!0,before:o,handler:c,after:l,replaced:u}}sortRegistrations(t,n){var r,i;return((r=t.priority)!=null?r:0)-((i=n.priority)!=null?i:0)||t.id-n.id||0}dispatchDomEvent(t,n,r){if(n!=null&&n.done)return;let i={hook:t,args:r,visit:n||this.swup.visit};document.dispatchEvent(new CustomEvent("swup:any",{detail:i,bubbles:!0})),document.dispatchEvent(new CustomEvent(`swup:${t}`,{detail:i,bubbles:!0}))}parseName(t){let[n,...r]=t.split(".");return[n,r.reduce((i,s)=>O({},i,{[s]:!0}),{})]}},Bu=e=>{if(e&&e.charAt(0)==="#"&&(e=e.substring(1)),!e)return null;let t=decodeURIComponent(e),n=document.getElementById(e)||document.getElementById(t)||Dn(`a[name='${CSS.escape(e)}']`)||Dn(`a[name='${CSS.escape(t)}']`);return n||e!=="top"||(n=document.body),n},Et="transition",Nn="animation";async function Qu({selector:e,elements:t}){if(e===!1&&!t)return;let n=[];if(t)n=Array.from(t);else if(e&&(n=qn(e,document.body),!n.length))return void console.warn(`[swup] No elements found matching animationSelector \`${e}\``);let r=n.map(i=>(function(s){let{type:o,timeout:a,propCount:l}=(function(u){let c=window.getComputedStyle(u),d=xt(c,`${Et}Delay`),h=xt(c,`${Et}Duration`),p=hs(d,h),g=xt(c,`${Nn}Delay`),m=xt(c,`${Nn}Duration`),v=hs(g,m),w=Math.max(p,v),x=w>0?p>v?Et:Nn:null;return{type:x,timeout:w,propCount:x?x===Et?h.length:m.length:0}})(s);return!(!o||!a)&&new Promise(u=>{let c=`${o}end`,d=performance.now(),h=0,p=()=>{s.removeEventListener(c,g),u()},g=m=>{m.target===s&&((performance.now()-d)/1e3=l&&p())};setTimeout(()=>{h0?await Promise.all(r):e&&console.warn(`[swup] No CSS animation duration defined on elements matching \`${e}\``)}function xt(e,t){return(e[t]||"").split(", ")}function hs(e,t){for(;e.lengthfs(n)+fs(e[r])))}function fs(e){return 1e3*parseFloat(e)}function qu(e,t={},n={}){if(typeof e!="string")throw new Error("swup.navigate() requires a URL parameter");if(this.shouldIgnoreVisit(e,{el:n.el,event:n.event}))return void window.location.assign(e);let{url:r,hash:i}=N.fromUrl(e),s=this.createVisit(O({},n,{to:r,hash:i}));this.performNavigation(s,t)}async function Fu(e,t={}){if(this.navigating){if(this.visit.state>=6)return e.state=2,void(this.onVisitEnd=()=>this.performNavigation(e,t));await this.hooks.call("visit:abort",this.visit,void 0),delete this.visit.to.document,this.visit.state=8}this.navigating=!0,this.visit=e;let{el:n}=e.trigger;t.referrer=t.referrer||this.location.url,t.animate===!1&&(e.animation.animate=!1),e.animation.animate||this.classes.clear();let r=t.history||ds(n,"data-swup-history");typeof r=="string"&&["push","replace"].includes(r)&&(e.history.action=r);let i=t.animation||ds(n,"data-swup-animation");var s,o;typeof i=="string"&&(e.animation.name=i),e.meta=t.meta||{},typeof t.cache=="object"?(e.cache.read=(s=t.cache.read)!=null?s:e.cache.read,e.cache.write=(o=t.cache.write)!=null?o:e.cache.write):t.cache!==void 0&&(e.cache={read:!!t.cache,write:!!t.cache}),delete t.cache;try{await this.hooks.call("visit:start",e,void 0),e.state=3;let a=this.hooks.call("page:load",e,{options:t},async(u,c)=>{let d;return u.cache.read&&(d=this.cache.get(u.to.url)),c.page=d||await this.fetchPage(u.to.url,c.options),c.cache=!!d,c.page});a.then(({html:u})=>{e.advance(5),e.to.html=u,e.to.document=new DOMParser().parseFromString(u,"text/html")});let l=e.to.url+e.to.hash;if(e.history.popstate||(e.history.action==="replace"||e.to.url===this.location.url?Pe(l):(this.currentHistoryIndex++,Ru(l,{index:this.currentHistoryIndex}))),this.location=N.fromUrl(l),e.history.popstate&&this.classes.add("is-popstate"),e.animation.name&&this.classes.add(`to-${ps(e.animation.name)}`),e.animation.wait&&await a,e.done||(await this.hooks.call("visit:transition",e,void 0,async()=>{if(!e.animation.animate)return await this.hooks.call("animation:skip",void 0),void await this.renderPage(e,await a);e.advance(4),await this.animatePageOut(e),e.animation.native&&document.startViewTransition?await document.startViewTransition(async()=>await this.renderPage(e,await a)).finished:await this.renderPage(e,await a),await this.animatePageIn(e)}),e.done))return;await this.hooks.call("visit:end",e,void 0,()=>this.classes.clear()),e.state=7,this.navigating=!1,this.onVisitEnd&&(this.onVisitEnd(),this.onVisitEnd=void 0)}catch(a){if(!a||a!=null&&a.aborted)return void(e.state=8);e.state=9,console.error(a),this.options.skipPopStateHandling=()=>(window.location.assign(e.to.url+e.to.hash),!0),window.history.back()}finally{delete e.to.document}}var Vu=async function(e){await this.hooks.call("animation:out:start",e,void 0,()=>{this.classes.add("is-changing","is-animating","is-leaving")}),await this.hooks.call("animation:out:await",e,{skip:!1},(t,{skip:n})=>{if(!n)return this.awaitAnimations({selector:t.animation.selector})}),await this.hooks.call("animation:out:end",e,void 0)},$u=function(e){var t;let n=e.to.document;if(!n)return!1;let r=((t=n.querySelector("title"))==null?void 0:t.innerText)||"";document.title=r;let i=qn('[data-swup-persist]:not([data-swup-persist=""])'),s=e.containers.map(o=>{let a=document.querySelector(o),l=n.querySelector(o);return a&&l?(a.replaceWith(l.cloneNode(!0)),!0):(a||console.warn(`[swup] Container missing in current document: ${o}`),l||console.warn(`[swup] Container missing in incoming document: ${o}`),!1)}).filter(Boolean);return i.forEach(o=>{let a=o.getAttribute("data-swup-persist"),l=Dn(`[data-swup-persist="${a}"]`);l&&l!==o&&l.replaceWith(o)}),s.length===e.containers.length},Uu=function(e){let t={behavior:"auto"},{target:n,reset:r}=e.scroll,i=n??e.to.hash,s=!1;return i&&(s=this.hooks.callSync("scroll:anchor",e,{hash:i,options:t},(o,{hash:a,options:l})=>{let u=this.getAnchorElement(a);return u&&u.scrollIntoView(l),!!u})),r&&!s&&(s=this.hooks.callSync("scroll:top",e,{options:t},(o,{options:a})=>(window.scrollTo(O({top:0,left:0},a)),!0))),s},ju=async function(e){if(e.done)return;let t=this.hooks.call("animation:in:await",e,{skip:!1},(n,{skip:r})=>{if(!r)return this.awaitAnimations({selector:n.animation.selector})});await ms(),await this.hooks.call("animation:in:start",e,void 0,()=>{this.classes.remove("is-animating")}),await t,await this.hooks.call("animation:in:end",e,void 0)},Wu=async function(e,t){if(e.done)return;e.advance(6);let{url:n}=t;this.isSameResolvedUrl(Oe(),n)||(Pe(n),this.location=N.fromUrl(n),e.to.url=this.location.url,e.to.hash=this.location.hash),await this.hooks.call("content:replace",e,{page:t},(r,{})=>{if(this.classes.remove("is-leaving"),r.animation.animate&&this.classes.add("is-rendering"),!this.replaceContent(r))throw new Error("[swup] Container mismatch, aborting");r.animation.animate&&(this.classes.add("is-changing","is-animating","is-rendering"),r.animation.name&&this.classes.add(`to-${ps(r.animation.name)}`))}),await this.hooks.call("content:scroll",e,void 0,()=>this.scrollToContent(e)),await this.hooks.call("page:view",e,{url:this.location.url,title:document.title})},zu=function(e){var t;if(t=e,!!t?.isSwupPlugin){if(e.swup=this,!e._checkRequirements||e._checkRequirements())return e._beforeMount&&e._beforeMount(),e.mount(),this.plugins.push(e),this.plugins}else console.error("Not a swup plugin instance",e)};function Gu(e){let t=this.findPlugin(e);if(t)return t.unmount(),t._afterUnmount&&t._afterUnmount(),this.plugins=this.plugins.filter(n=>n!==t),this.plugins;console.error("No such plugin",t)}function Ku(e){return this.plugins.find(t=>t===e||t.name===e||t.name===`Swup${String(e)}`)}function Yu(e){if(typeof this.options.resolveUrl!="function")return console.warn("[swup] options.resolveUrl expects a callback function."),e;let t=this.options.resolveUrl(e);return t&&typeof t=="string"?t.startsWith("//")||t.startsWith("http")?(console.warn("[swup] options.resolveUrl needs to return a relative url"),e):t:(console.warn("[swup] options.resolveUrl needs to return a url"),e)}function Ju(e,t){return this.resolveUrl(e)===this.resolveUrl(t)}var Xu={animateHistoryBrowsing:!1,animationSelector:'[class*="transition-"]',animationScope:"html",cache:!0,containers:["#swup"],hooks:{},ignoreVisit:(e,{el:t}={})=>!(t==null||!t.closest("[data-no-swup]")),linkSelector:"a[href]",linkToSelf:"scroll",native:!1,plugins:[],resolveUrl:e=>e,requestHeaders:{"X-Requested-With":"swup",Accept:"text/html, application/xhtml+xml"},skipPopStateHandling:e=>{var t;return((t=e.state)==null?void 0:t.source)!=="swup"},timeout:0},St=class{get currentPageUrl(){return this.location.url}constructor(t={}){var n,r;this.version="4.8.2",this.options=void 0,this.defaults=Xu,this.plugins=[],this.visit=void 0,this.cache=void 0,this.hooks=void 0,this.classes=void 0,this.location=N.fromUrl(window.location.href),this.currentHistoryIndex=void 0,this.clickDelegate=void 0,this.navigating=!1,this.onVisitEnd=void 0,this.use=zu,this.unuse=Gu,this.findPlugin=Ku,this.log=()=>{},this.navigate=qu,this.performNavigation=Fu,this.createVisit=Du,this.delegateEvent=Mu,this.fetchPage=Nu,this.awaitAnimations=Qu,this.renderPage=Wu,this.replaceContent=$u,this.animatePageIn=ju,this.animatePageOut=Vu,this.scrollToContent=Uu,this.getAnchorElement=Bu,this.getCurrentUrl=Oe,this.resolveUrl=Yu,this.isSameResolvedUrl=Ju,this.options=O({},this.defaults,t),this.handleLinkClick=this.handleLinkClick.bind(this),this.handlePopState=this.handlePopState.bind(this),this.cache=new Hn(this),this.classes=new Bn(this),this.hooks=new Qn(this),this.visit=this.createVisit({to:""}),this.currentHistoryIndex=(n=(r=window.history.state)==null?void 0:r.index)!=null?n:1,this.enable()}async enable(){var t;let{linkSelector:n}=this.options;this.clickDelegate=this.delegateEvent(n,"click",this.handleLinkClick),window.addEventListener("popstate",this.handlePopState),this.options.animateHistoryBrowsing&&(window.history.scrollRestoration="manual"),this.options.native=this.options.native&&!!document.startViewTransition,this.options.plugins.forEach(r=>this.use(r));for(let[r,i]of Object.entries(this.options.hooks)){let[s,o]=this.hooks.parseName(r);this.hooks.on(s,i,o)}((t=window.history.state)==null?void 0:t.source)!=="swup"&&Pe(null,{index:this.currentHistoryIndex}),await ms(),await this.hooks.call("enable",void 0,void 0,()=>{let r=document.documentElement;r.classList.add("swup-enabled"),r.classList.toggle("swup-native",this.options.native)})}async destroy(){this.clickDelegate.destroy(),window.removeEventListener("popstate",this.handlePopState),this.cache.clear(),this.options.plugins.forEach(t=>this.unuse(t)),await this.hooks.call("disable",void 0,void 0,()=>{let t=document.documentElement;t.classList.remove("swup-enabled"),t.classList.remove("swup-native")}),this.hooks.clear()}shouldIgnoreVisit(t,{el:n,event:r}={}){let{origin:i,url:s,hash:o}=N.fromUrl(t);return i!==window.location.origin||!(!n||!this.triggerWillOpenNewWindow(n))||!!this.options.ignoreVisit(s+o,{el:n,event:r})}handleLinkClick(t){let n=t.delegateTarget,{href:r,url:i,hash:s}=N.fromElement(n);if(this.shouldIgnoreVisit(r,{el:n,event:t}))return;if(this.navigating&&i===this.visit.to.url)return void t.preventDefault();let o=this.createVisit({to:i,hash:s,el:n,event:t});t.metaKey||t.ctrlKey||t.shiftKey||t.altKey?this.hooks.callSync("link:newtab",o,{href:r}):t.button===0&&this.hooks.callSync("link:click",o,{el:n,event:t},()=>{var a;let l=(a=o.from.url)!=null?a:"";t.preventDefault(),i&&i!==l?this.isSameResolvedUrl(i,l)||this.performNavigation(o):s?this.hooks.callSync("link:anchor",o,{hash:s},()=>{Pe(i+s),this.scrollToContent(o)}):this.hooks.callSync("link:self",o,void 0,()=>{this.options.linkToSelf==="navigate"?this.performNavigation(o):(Pe(i),this.scrollToContent(o))})})}handlePopState(t){var n,r,i,s;let o=(n=(r=t.state)==null?void 0:r.url)!=null?n:window.location.href;if(this.options.skipPopStateHandling(t)||this.isSameResolvedUrl(Oe(),this.location.url))return;let{url:a,hash:l}=N.fromUrl(o),u=this.createVisit({to:a,hash:l,event:t});u.history.popstate=!0;let c=(i=(s=t.state)==null?void 0:s.index)!=null?i:0;c&&c!==this.currentHistoryIndex&&(u.history.direction=c-this.currentHistoryIndex>0?"forwards":"backwards",this.currentHistoryIndex=c),u.animation.animate=!1,u.scroll.reset=!1,u.scroll.target=!1,this.options.animateHistoryBrowsing&&(u.animation.animate=!0,u.scroll.reset=!0),this.hooks.callSync("history:popstate",u,{event:t},()=>{this.performNavigation(u)})}triggerWillOpenNewWindow(t){return!!t.matches('[download], [target="_blank"]')}};function Ae(){return Ae=Object.assign?Object.assign.bind():function(e){for(var t=1;tString(e).split(".").map(t=>String(parseInt(t||"0",10))).concat(["0","0"]).slice(0,3).join("."),ve=class{constructor(){this.isSwupPlugin=!0,this.swup=void 0,this.version=void 0,this.requires={},this.handlersToUnregister=[]}mount(){}unmount(){this.handlersToUnregister.forEach(t=>t()),this.handlersToUnregister=[]}_beforeMount(){if(!this.name)throw new Error("You must define a name of plugin when creating a class.")}_afterUnmount(){}_checkRequirements(){return typeof this.requires!="object"||Object.entries(this.requires).forEach(([t,n])=>{if(!(function(r,i,s){let o=(function(a,l){var u;if(a==="swup")return(u=l.version)!=null?u:"";{var c;let d=l.findPlugin(a);return(c=d?.version)!=null?c:""}})(r,s);return!!o&&((a,l)=>l.every(u=>{let[,c,d]=u.match(/^([\D]+)?(.*)$/)||[];var h,p;return((g,m)=>{let v={"":w=>w===0,">":w=>w>0,">=":w=>w>=0,"<":w=>w<0,"<=":w=>w<=0};return(v[m]||v[""])(g)})((p=d,h=vs(h=a),p=vs(p),h.localeCompare(p,void 0,{numeric:!0})),c||">=")}))(o,i)})(t,n=Array.isArray(n)?n:[n],this.swup)){let r=`${t} ${n.join(", ")}`;throw new Error(`Plugin version mismatch: ${this.name} requires ${r}`)}}),!0}on(t,n,r={}){var i;n=!(i=n).name.startsWith("bound ")||i.hasOwnProperty("prototype")?n.bind(this):n;let s=this.swup.hooks.on(t,n,r);return this.handlersToUnregister.push(s),s}once(t,n,r={}){return this.on(t,n,Ae({},r,{once:!0}))}before(t,n,r={}){return this.on(t,n,Ae({},r,{before:!0}))}replace(t,n,r={}){return this.on(t,n,Ae({},r,{replace:!0}))}off(t,n){return this.swup.hooks.off(t,n)}};(function(){if(!(typeof window>"u"||typeof document>"u"||typeof HTMLElement>"u")){var e=!1;try{var t=document.createElement("div");t.addEventListener("focus",function(s){s.preventDefault(),s.stopPropagation()},!0),t.focus(Object.defineProperty({},"preventScroll",{get:function(){if(navigator&&typeof navigator.userAgent<"u"&&navigator.userAgent&&navigator.userAgent.match(/Edge\/1[7-8]/))return e=!1;e=!0}}))}catch{}if(HTMLElement.prototype.nativeFocus===void 0&&!e){HTMLElement.prototype.nativeFocus=HTMLElement.prototype.focus;var n=function(s){for(var o=s.parentNode,a=[],l=document.scrollingElement||document.documentElement;o&&o!==l;)(o.offsetHeightn.replace(`{${r}}`,t[r]||""),e||"")}var Vn=class{constructor(){var t;this.id="swup-announcer",this.style="position:absolute;top:0;left:0;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden;white-space:nowrap;word-wrap:normal;width:1px;height:1px;",this.region=void 0,this.region=(t=this.getRegion())!=null?t:this.createRegion()}getRegion(){return document.getElementById(this.id)}createRegion(){let t=(function(n){let r=document.createElement("template");return r.innerHTML=n,r.content.children[0]})(`

`);return document.body.appendChild(t),t}announce(t,n=0){return new Promise(r=>{setTimeout(()=>{this.region.textContent===t&&(t=`${t}.`),this.region.textContent="",this.region.textContent=t,r()},n)})}};function ws(e){let t;if(t=typeof e=="string"?document.querySelector(e):e,!(t instanceof HTMLElement))return;let n=t.getAttribute("tabindex");t.setAttribute("tabindex","-1"),t.focus({preventScroll:!0}),n!==null&&t.setAttribute("tabindex",n)}var _t=class extends ve{constructor(t={}){super(),this.name="SwupA11yPlugin",this.requires={swup:">=4"},this.defaults={headingSelector:"h1",respectReducedMotion:!0,autofocus:!1,announcements:{visit:"Navigated to: {title}",url:"New page at {url}"}},this.options=void 0,this.announcer=void 0,this.announcementDelay=100,this.rootSelector="body",this.handleAnchorScroll=(n,{hash:r})=>{let i=this.swup.getAnchorElement(r);i instanceof HTMLElement&&ws(i)},this.options=Fn({},this.defaults,t),this.announcer=new Vn}mount(){this.swup.hooks.create("content:announce"),this.swup.hooks.create("content:focus"),this.before("visit:start",this.prepareVisit),this.on("visit:start",this.markAsBusy),this.on("visit:end",this.unmarkAsBusy),this.on("visit:end",this.focusContent),this.on("visit:end",this.announceContent),this.on("scroll:anchor",this.handleAnchorScroll),this.before("visit:start",this.disableAnimations),this.before("link:self",this.disableAnimations),this.before("link:anchor",this.disableAnimations),this.swup.announce=this.announce.bind(this)}unmount(){this.swup.announce=void 0}async announce(t){await this.announcer.announce(t)}markAsBusy(){document.documentElement.setAttribute("aria-busy","true")}unmarkAsBusy(){document.documentElement.removeAttribute("aria-busy")}prepareVisit(t){t.a11y={announce:void 0,focus:this.rootSelector}}announceContent(t){this.swup.hooks.callSync("content:announce",t,void 0,n=>{n.a11y.announce===void 0&&(n.a11y.announce=this.getPageAnnouncement()),n.a11y.announce&&this.announcer.announce(n.a11y.announce,this.announcementDelay)})}focusContent(t){this.swup.hooks.callSync("content:focus",t,void 0,n=>{n.a11y.focus&&(this.options.autofocus&&(function(){let r=(function(){let i=document.querySelector("body [autofocus]");if(i&&!i.closest('[inert], [aria-disabled], [aria-hidden="true"]'))return i})();return!!r&&(r!==document.activeElement&&r.focus(),!0)})()===!0||ws(n.a11y.focus))})}getPageAnnouncement(){let{headingSelector:t,announcements:n}=this.options;return(function({headingSelector:r="h1",announcements:i={}}){var s,o;let a=document.documentElement.lang||"*",{href:l,url:u,pathname:c}=N.fromUrl(window.location.href),d=(s=(o=i[a])!=null?o:i["*"])!=null?s:i;if(typeof d!="object")return;let h=document.querySelector(r);h||console.warn(`SwupA11yPlugin: No main heading (${r}) found on new page`);let p=h?.getAttribute("aria-label")||h?.textContent||document.title||ys(d.url,{href:l,url:u,path:c});return ys(d.visit,{title:p,href:l,url:u,path:c})})({headingSelector:t,announcements:n})}disableAnimations(t){this.options.respectReducedMotion&&window.matchMedia("(prefers-reduced-motion: reduce)").matches&&(t.animation.animate=!1,t.scroll.animate=!1)}};function $n(){return $n=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let a=Math.random()*this.trickleValue;this.setValue(this.value+a)},t!==void 0&&(this.className=String(t)),n!==void 0&&(this.styleAttr=String(n)),r!==void 0&&(this.animationDuration=Number(r)),i!==void 0&&(this.minValue=Number(i)),s!==void 0&&(this.initialValue=Number(s)),o!==void 0&&(this.trickleValue=Number(o)),this.styleElement=this.createStyleElement(),this.progressElement=this.createProgressElement()}get defaultStyles(){return` + .${this.className} { + position: fixed; + display: block; + top: 0; + left: 0; + width: 100%; + height: 3px; + background-color: black; + z-index: 9999; + transition: + transform ${this.animationDuration}ms ease-out, + opacity ${this.animationDuration/2}ms ${this.animationDuration/2}ms ease-in; + transform: translate3d(0, 0, 0) scaleX(var(--progress, 0)); + transform-origin: 0; + } + `}show(){this.visible||(this.visible=!0,this.installStyleElement(),this.installProgressElement(),this.startTrickling())}hide(){this.visible&&!this.hiding&&(this.hiding=!0,this.fadeProgressElement(()=>{this.uninstallProgressElement(),this.stopTrickling(),this.visible=!1,this.hiding=!1}))}setValue(t){this.value=Math.min(1,Math.max(this.minValue,t)),this.refresh()}installStyleElement(){document.head.prepend(this.styleElement)}installProgressElement(){this.progressElement.style.setProperty("--progress",String(0)),this.progressElement.style.opacity="1",document.body.prepend(this.progressElement),this.progressElement.scrollTop=0,this.setValue(Math.random()*this.initialValue)}fadeProgressElement(t){this.progressElement.style.opacity="0",setTimeout(t,1.5*this.animationDuration)}uninstallProgressElement(){this.progressElement.remove()}startTrickling(){this.trickleInterval||(this.trickleInterval=window.setInterval(this.trickle,this.animationDuration))}stopTrickling(){window.clearInterval(this.trickleInterval),delete this.trickleInterval}refresh(){requestAnimationFrame(()=>{this.progressElement.style.setProperty("--progress",String(this.value))})}createStyleElement(){let t=document.createElement("style");return this.styleAttr.split(" ").forEach(n=>t.setAttribute(n,"")),t.textContent=this.defaultStyles,t}createProgressElement(){let t=document.createElement("div");return t.className=this.className,t.setAttribute("aria-hidden","true"),t}},Lt=class extends ve{constructor(t={}){super(),this.name="SwupProgressPlugin",this.defaults={className:"swup-progress-bar",delay:300,transition:300,minValue:.1,initialValue:.25,finishAnimation:!0},this.options=void 0,this.progressBar=void 0,this.showProgressBarTimeout=void 0,this.hideProgressBarTimeout=void 0,this.options=$n({},this.defaults,t);let{className:n,minValue:r,initialValue:i,transition:s}=this.options;this.progressBar=new Un({className:n,minValue:r,initialValue:i,animationDuration:s})}mount(){this.on("visit:start",this.startShowingProgress),this.on("page:view",this.stopShowingProgress)}startShowingProgress(){this.progressBar.setValue(0),this.showProgressBarAfterDelay()}stopShowingProgress(){this.progressBar.setValue(1),this.options.finishAnimation?this.finishAnimationAndHideProgressBar():this.hideProgressBar()}showProgressBar(){this.cancelHideProgressBarTimeout(),this.progressBar.show()}showProgressBarAfterDelay(){this.cancelShowProgressBarTimeout(),this.cancelHideProgressBarTimeout(),this.showProgressBarTimeout=window.setTimeout(this.showProgressBar.bind(this),this.options.delay)}hideProgressBar(){this.cancelShowProgressBarTimeout(),this.progressBar.hide()}finishAnimationAndHideProgressBar(){this.cancelShowProgressBarTimeout(),this.hideProgressBarTimeout=window.setTimeout(this.hideProgressBar.bind(this),this.options.transition)}cancelShowProgressBarTimeout(){window.clearTimeout(this.showProgressBarTimeout),delete this.showProgressBarTimeout}cancelHideProgressBarTimeout(){window.clearTimeout(this.hideProgressBarTimeout),delete this.hideProgressBarTimeout}};var bs=()=>{window.dispatchEvent(new Event("exdoc:loaded"))},Zu=(e,{page:t})=>{//i.test(t.html)&&(e.abort(),window.location.reload())};window.addEventListener("DOMContentLoaded",bs);!C&&window.location.protocol!=="file:"&&new St({animationSelector:!1,containers:["#main"],ignoreVisit:e=>{let t=e.split("#")[0];return t===window.location.pathname||t===window.location.pathname+".html"},linkSelector:'a[href]:not([href^="/"]):not([href^="http"])[href$=".html"]',hooks:{"page:load":Zu,"page:view":bs},plugins:[new _t,new Lt({delay:500})]});})(); +/*! Bundled license information: + +lunr/lunr.js: + (** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + *) + (*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + *) + (*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + *) + (*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + *) +*/ diff --git a/doc/dist/html-erlang-KCHZLXSC.css b/doc/dist/html-erlang-KCHZLXSC.css new file mode 100644 index 0000000..4dd7edb --- /dev/null +++ b/doc/dist/html-erlang-KCHZLXSC.css @@ -0,0 +1,6 @@ +:root{--main: hsl(0, 100%, 44%);--mainDark: hsl(0, 100%, 34%);--mainDarkest: hsl(0, 100%, 24%);--mainLight: hsl(0, 100%, 64%);--mainLightest: hsl(0, 100%, 74%);--searchBarFocusColor: hsl(0, 100%, 50%);--searchBarBorderColor: rgb(255, 71, 71, .1);--link-color: hsl(212, 96%, 45%);--link-visited-color: hsl(212, 96%, 40%)}body.dark{--link-color: hsl(212, 56%, 72%);--link-visited-color: hsl(212, 56%, 67%)}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-ext-400-normal-N27NCBWW.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-400-normal-W7754I4D.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-ext-700-normal-Q2L5DVMW.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-700-normal-2XVSBPG4.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--content-width: 949px;--content-gutter: 60px;--borderRadius-lg: 14px;--borderRadius-base: 8px;--borderRadius-sm: 3px;--navTabBorderWidth: 2px;--sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;--baseLineHeight: 1.5em;--gray10: hsl(207, 43%, 98.5%);--gray25: hsl(207, 43%, 98%);--gray50: hsl(207, 43%, 96%);--gray100: hsl(212, 33%, 91%);--gray200: hsl(210, 29%, 88%);--gray300: hsl(210, 26%, 76%);--gray400: hsl(210, 21%, 64%);--gray450: hsl(210, 21%, 49%);--gray500: hsl(210, 21%, 34%);--gray600: hsl(210, 27%, 26%);--gray650: hsl(211, 32%, 22%);--gray700: hsl(212, 35%, 17%);--gray750: hsl(214, 46%, 14%);--gray800: hsl(216, 52%, 11%);--gray800-opacity-0: hsla(216, 52%, 11%, 0%);--gray850: hsl(216, 63%, 8%);--gray900: hsl(218, 73%, 4%);--gray900-opacity-50: hsla(218, 73%, 4%, 50%);--gray900-opacity-0: hsla(218, 73%, 4%, 0%);--coldGrayFaint: hsl(240, 5%, 97%);--coldGrayLight: hsl(240, 5%, 88%);--coldGray-lightened-10: hsl(240, 5%, 56%);--coldGray: hsl(240, 5%, 46%);--coldGray-opacity-10: hsla(240, 5%, 46%, 10%);--coldGrayDark: hsl(240, 5%, 28%);--coldGrayDim: hsl(240, 5%, 18%);--yellowLight: hsl(43, 100%, 95%);--yellowDark: hsl(44, 100%, 15%);--yellow: hsl(60, 100%, 43%);--green-lightened-10: hsl(90, 100%, 45%);--green: hsl(90, 100%, 35%);--white: hsl(0, 0%, 100%);--white-opacity-80: hsla(0, 0%, 100%, 80%);--white-opacity-50: hsla(0, 0%, 100%, 50%);--white-opacity-10: hsla(0, 0%, 100%, 10%);--white-opacity-0: hsla(0, 0%, 100%, 0%);--black: hsl(0, 0%, 0%);--black-opacity-80: hsla(0, 0%, 0%, 80%);--black-opacity-50: hsla(0, 0%, 0%, 50%);--black-opacity-30: hsla(0, 0%, 0%, 30%);--black-opacity-10: hsla(0, 0%, 0%, 10%);--black-opacity-0: hsla(0, 0%, 0%, 10%);--orangeDark: hsl(30, 90%, 40%);--orangeLight: hsl(30, 80%, 50%);--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--transition-duration: .15s;--transition-timing: cubic-bezier(.4, 0, .2, 1);--transition-all: all var(--transition-duration) var(--transition-timing);--transition-colors: color var(--transition-duration) var(--transition-timing), background-color var(--transition-duration) var(--transition-timing), border-color var(--transition-duration) var(--transition-timing), text-decoration-color var(--transition-duration) var(--transition-timing), fill var(--transition-duration) var(--transition-timing), stroke var(--transition-duration) var(--transition-timing);--transition-opacity: opacity var(--transition-duration) var(--transition-timing)}@media screen and (max-width:768px){:root{--content-width: 100%;--content-gutter: 20px}}option{background-color:var(--sidebarBackground)}:root{--background: var(--white);--contrast: var(--black);--textBody: var(--gray800);--textHeaders: var(--gray900);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--coldGrayFaint);--iconAction: var(--coldGray);--iconActionHover: var(--gray800);--blockquoteBackground: var(--coldGrayFaint);--blockquoteBorder: var(--coldGrayLight);--tableHeadBorder: var(--gray100);--tableBodyBorder: var(--gray50);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray100);--admMixAmt: 25%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 100%, 96%) 100%);--warningHeading: hsl(var(--warningHue), 95%, 38%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 85%, 96%) 100%);--errorHeading: hsl(var(--errorHue), 80%, 40%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 90%, 96%) 100%);--infoHeading: hsl(var(--infoHue), 60%, 30%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 93%) 100%);--neutralHeading: hsl(var(--neutralHue), 35%, 30%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 93%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 25%);--fnSpecAttr: var(--coldGray);--fnDeprecated: var(--yellowLight);--blink: var(--yellowLight);--codeBackground: var(--gray10);--codeBorder: var(--gray100);--codeScrollThumb: var(--gray400);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--white-opacity-80);--admCodeBorder: var(--black-opacity-10);--admCodeColor: var(--black);--tabBorder: var(--gray300);--tabBorderTop: var(--gray100);--tabShadow: var(--gray25);--bottomActionsBtnBorder: var(--black-opacity-10);--bottomActionsBtnTitle: var(--mainDark);--modalBackground: var(--white);--settingsInput: var(--gray500);--settingsInputBackground: var(--white);--settingsInputBorder: var(--gray300);--settingsSectionBorder: var(--gray300);--quickSwitchInput: var(--gray500);--quickSwitchContour: var(--coldGray);--success: var(--green);--progressBarColor: var(--gray400);--sidebarAccentMain: var(--black);--sidebarBackground: var(--gray50);--sidebarHeader: var(--gray100);--sidebarMuted: var(--gray800);--sidebarHover: var(--black);--sidebarStaleVersion: var(--orangeDark);--sidebarSubheadings: var(--gray500);--sidebarItem: var(--black);--sidebarInactiveItemBorder: var(--gray500);--sidebarInactiveItemMarker: var(--gray200);--sidebarLanguageAccentBar: var(--mainDark);--sidebarActiveItem: var(--mainDarkest);--searchBarBorder: var(--gray200);--searchAccentMain: var(--gray600);--searchLanguageAccentBar: var(--main);--searchSearch: var(--white);--autocompleteBorder: rgba(3, 9, 19, .1);--autocompletePreview: var(--gray25);--autocompleteSelected: var(--gray25);--autocompleteHover: var(--gray50);--autocompleteBackground: var(--white);--suggestionBorder: var(--gray200);--autocompleteResults: var(--gray600);--autocompleteResultsBold: var(--gray800);--autocompleteLabelBack: var(--gray100);--autocompleteLabelFont: var(--gray600)}body.dark{--background: var(--gray900);--contrast: var(--white);--textBody: var(--gray200);--textHeaders: var(--gray100);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--gray700);--iconAction: var(--coldGray-lightened-10);--iconActionHover: var(--white);--blockquoteBackground: var(--coldGray-opacity-10);--blockquoteBorder: var(--coldGrayDim);--tableHeadBorder: var(--gray600);--tableBodyBorder: var(--gray700);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray900);--admMixAmt: 40%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 40%, 14%) 100%);--warningHeading: hsl(var(--warningHue), 100%, 50%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 40%, 14%) 100%);--errorHeading: hsl(var(--errorHue), 100%, 52%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 60%, 15%) 100%);--infoHeading: hsl(var(--infoHue), 100%, 52%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 14%) 100%);--neutralHeading: hsl(var(--neutralHue), 50%, 80%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 14%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 48%);--fnSpecAttr: var(--gray400);--fnDeprecated: var(--yellowDark);--blink: var(--gray600);--codeBackground: var(--gray800);--codeBorder: var(--gray650);--codeScrollThumb: var(--gray500);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--black-opacity-30);--admCodeBorder: var(--white-opacity-10);--admCodeColor: var(--gray100);--tabBorder: var(--gray700);--tabBorderTop: var(--gray700);--tabShadow: var(--black);--bottomActionsBtnBorder: var(--white-opacity-10);--bottomActionsBtnTitle: var(--mainLightest);--modalBackground: var(--gray800);--settingsInput: var(--white);--settingsInputBackground: var(--gray700);--settingsInputBorder: var(--gray700);--settingsSectionBorder: var(--gray700);--quickSwitchInput: var(--gray300);--quickSwitchContour: var(--gray500);--success: var(--green-lightened-10);--progressBarColor: var(--gray300);--sidebarAccentMain: var(--gray50);--sidebarBackground: var(--gray800);--sidebarHeader: var(--gray700);--sidebarMuted: var(--gray300);--sidebarHover: var(--white);--sidebarStaleVersion: var(--orangeLight);--sidebarSubheadings: var(--gray400);--sidebarItem: var(--gray200);--sidebarInactiveItemBorder: var(--gray400);--sidebarInactiveItemMarker: var(--gray600);--sidebarLanguageAccentBar: var(--mainLight);--sidebarActiveItem: var(--mainLightest);--searchBarBorder: var(--gray500);--searchAccentMain: var(--gray300);--searchSearch: var(--gray900);--autocompleteBorder: rgba(28,42,60,.75);--autocompletePreview: var(--gray750);--autocompleteSelected: var(--gray750);--autocompleteHover: var(--gray700);--autocompleteBackground: var(--gray800);--suggestionBorder: var(--gray600);--autocompleteResults: var(--gray200);--autocompleteResultsBold: var(--gray100);--autocompleteLabelBack: var(--gray600);--autocompleteLabelFont: rgba(255, 255, 255, .8)}:root:has(body.dark){color-scheme:dark}*,:before,:after{box-sizing:border-box}html{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";line-height:1.15;-webkit-text-size-adjust:100%;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}@font-face{font-family:remixicon;src:url("./remixicon-ZWU5GQ4X.woff2") format("woff2");font-display:swap}[class^=ri-],[class*=" ri-"],.remix-icon{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:root{--icon-arrow-up-s: "\ea78";--icon-arrow-down-s: "\ea4e";--icon-arrow-right-s: "\ea6e";--icon-add: "\ea13";--icon-subtract: "\f1af";--icon-error-warning: "\eca1";--icon-external-link-line: "\ecaf";--icon-information: "\ee59";--icon-alert: "\ea21";--icon-double-quotes-l: "\ec51";--icon-link-m: "\eeaf";--icon-close-line: "\eb99";--icon-code-s-slash-line: "\ebad";--icon-menu-line: "\ef3e";--icon-search-2-line: "\f0cd";--icon-settings-3-line: "\f0e6";--icon-printer-line: "\f029";--icon-markdown-line: "\ef1e"}.ri-lg{font-size:1.3333em;line-height:.75em;vertical-align:-.0667em}.ri-settings-3-line:before{content:var(--icon-settings-3-line)}.ri-add-line:before{content:var(--icon-add)}.ri-subtract-line:before{content:var(--icon-subtract)}.ri-arrow-up-s-line:before{content:var(--icon-arrow-up-s)}.ri-arrow-down-s-line:before{content:var(--icon-arrow-down-s)}.ri-arrow-right-s-line:before{content:var(--icon-arrow-right-s)}.ri-external-link-line:before{content:var(--icon-external-link-line)}.ri-search-2-line:before{content:var(--icon-search-2-line)}.ri-menu-line:before{content:var(--icon-menu-line)}.ri-close-line:before{content:var(--icon-close-line)}.ri-link-m:before{content:var(--icon-link-m)}.ri-code-s-slash-line:before{content:var(--icon-code-s-slash-line)}.ri-error-warning-line:before{content:var(--icon-error-warning)}.ri-information-line:before{content:var(--icon-information)}.ri-alert-line:before{content:var(--icon-alert)}.ri-double-quotes-l:before{content:var(--icon-double-quotes-l)}.ri-printer-line:before{content:var(--icon-printer-line)}.ri-markdown-line:before{content:var(--icon-markdown-line)}html,body{box-sizing:border-box;height:100%;width:100%}body{--sidebarWidth: 300px;--sidebarMinWidth: 300px;--sidebarTransitionDuration: .3s;background-color:var(--background);color:var(--textBody);font-size:var(--text-md);line-height:1.6875em;outline:none!important}*,*:before,*:after{box-sizing:inherit}.body-wrapper{display:flex;height:100%}.sidebar{display:none;flex-direction:column;width:var(--sidebarWidth);min-width:var(--sidebarMinWidth);max-width:50vw;height:100%;position:fixed;top:0;left:calc(-1 * var(--sidebarWidth));z-index:100;resize:horizontal}.sidebar-button{padding:20px 12px 18px 19px;position:fixed;z-index:200;top:0;left:0;will-change:transform;transform:translate(0)}.content{left:0;width:100%;height:100%;position:absolute}.content .content-inner{container:content / inline-size;max-width:var(--content-width);min-height:100%;margin:0 auto;padding:0 var(--content-gutter) 10px}.content-inner:focus{outline:none}.sidebar-transition .sidebar,.sidebar-transition .sidebar-button,.sidebar-transition .content{transition:all var(--sidebarTransitionDuration) ease-in-out allow-discrete}.sidebar-open .sidebar,.sidebar-transition .sidebar{display:flex}.sidebar-open .sidebar{left:0}.sidebar-open .sidebar-button{transform:translate(calc(var(--sidebarWidth) - 100%))}.sidebar-open .content{width:calc(100% - var(--sidebarWidth));left:var(--sidebarWidth)}@media screen and (max-width:768px){.sidebar-open .content{left:0;width:100%}.sidebar{max-width:90vw}body:not(.sidebar-open) .sidebar-button{position:absolute}}.swup-progress-bar{height:2px;background-color:var(--progressBarColor)}.sidebar{--sidebarFontSize: 16px;--sidebarLineHeight: 20px;font-family:var(--sansFontFamily);font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);background-color:var(--sidebarBackground);color:var(--sidebarAccentMain);overflow:hidden;.sidebar-tabpanel{scrollbar-width:thin}}.apple-os .sidebar{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sidebar ul{list-style:none}.sidebar ul li{margin:0;padding:0 10px}.sidebar a{color:var(--sidebarAccentMain);text-decoration:none;transition:var(--transition-colors)}.sidebar a:hover{color:var(--sidebarHover)}.sidebar .external-link{margin-left:2.5px}.sidebar .sidebar-header{background-color:var(--sidebarHeader);width:100%}.sidebar .sidebar-projectInfo{display:flex;justify-content:start;align-items:center;gap:8px;margin:8px 34px 12px 14px}.sidebar .sidebar-projectInfo>div{flex:1}.sidebar .sidebar-projectImage{align-self:flex-end}.sidebar .sidebar-projectImage img{display:block;max-width:48px;max-height:48px}.sidebar .sidebar-projectName{font-weight:700;font-size:var(--text-xl);line-height:24px;color:var(--sidebarAccentMain);margin:0;padding:0;word-wrap:break-word;display:block;width:calc(100% - 12px)}.sidebar .sidebar-projectVersion{display:block;position:relative;margin:0;padding:0;font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);color:var(--sidebarMuted);width:calc(100% - 12px)}.sidebar .sidebar-projectVersion form{display:flex}.sidebar .sidebar-projectVersion select{cursor:pointer;position:relative;margin:0;padding:0 0 0 10px;border:none;-webkit-appearance:none;appearance:none;background-color:transparent;color:var(--sidebarMuted);z-index:2}.sidebar .sidebar-projectVersion option{color:initial}.sidebar .sidebar-projectVersionsCaret{position:absolute;left:0;top:2px;z-index:1;font-size:8px;color:var(--sidebarMuted)}.sidebar .sidebar-projectVersion select::-ms-expand{display:none}.sidebar .sidebar-staleVersion{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--sidebarStaleVersion)}.sidebar .sidebar-staleVersion>a{color:var(--sidebarStaleVersion);font-weight:400}.sidebar .sidebar-staleIcon{font-size:var(--text-lg);line-height:0}.sidebar .sidebar-list-nav{display:flex;margin:0;padding:0;overflow:auto;scrollbar-width:thin}.sidebar .sidebar-list-nav :is(li,li button){text-transform:uppercase;letter-spacing:.02em;font-size:var(--text-sm);color:var(--sidebarSubheadings);white-space:nowrap}.sidebar .sidebar-list-nav li{display:inline-block;padding:0}.sidebar .sidebar-list-nav button{background:none;border:0;border-radius:0;-webkit-appearance:none;text-align:inherit;color:inherit;font-weight:inherit;cursor:pointer;display:inline-block;line-height:27px;padding:4px 14px;transition:var(--transition-all)}.sidebar .sidebar-list-nav button{border-bottom:var(--navTabBorderWidth) solid transparent}.sidebar .sidebar-list-nav button:not([aria-selected]):hover{border-bottom:var(--navTabBorderWidth) solid var(--sidebarInactiveItemBorder);color:var(--sidebarAccentMain);transition:var(--transition-all)}.sidebar .sidebar-list-nav button[aria-selected]{border-bottom:var(--navTabBorderWidth) solid var(--sidebarLanguageAccentBar);color:var(--sidebarAccentMain)}.sidebar .sidebar-tabpanel{flex:1 1 .01%;overflow-y:auto;overscroll-behavior:contain;position:relative;-webkit-overflow-scrolling:touch;padding-top:12px;scroll-padding-top:40px}.sidebar .full-list{margin:0;padding:0 0 20px;position:relative}.sidebar .full-list :is(li,a){display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.sidebar .full-list li{padding:0;line-height:27px}.sidebar .full-list li.group{text-transform:uppercase;font-weight:700;font-size:.8em;margin:1.5em 0 0;line-height:1.8em;color:var(--sidebarSubheadings);padding-left:15px}.sidebar .full-list li.nesting-context{font-weight:700;font-size:.9em;line-height:1.8em;color:var(--sidebarSubheadings);margin-top:10px;padding-left:15px}.sidebar .full-list a{margin-right:30px;padding:3px 0 3px 12px;border-left:var(--navTabBorderWidth) solid transparent;color:var(--sidebarItem)}.sidebar .full-list a[aria-selected]{color:var(--sidebarActiveItem)}.sidebar .full-list button{appearance:none;background-color:transparent;border:0;padding:0;cursor:pointer;color:inherit;width:20px;text-align:center;font-size:calc(1.2 * var(--sidebarFontSize));line-height:var(--sidebarLineHeight);position:absolute;display:block;right:10px;transform:translateY(-100%)}.sidebar .full-list a[aria-selected]+button{color:var(--sidebarActiveItem)}.sidebar .full-list button:after{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:var(--icon-arrow-down-s)}.sidebar .full-list button[aria-expanded=true]:after{content:var(--icon-arrow-up-s)}.sidebar .full-list ul{display:none;margin:10px 0 10px 10px;padding:0}.sidebar .full-list button[aria-expanded=true]+ul{display:block}.sidebar .full-list>li>a{height:27px;line-height:var(--sidebarLineHeight)}.sidebar .full-list>li>a:hover{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li>a[aria-selected]{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li:last-child{margin-bottom:30px}.sidebar .full-list>li.group:first-child{margin-top:0}.sidebar .full-list>li>ul>li:not(:has(li a[aria-selected=true]))>a[aria-selected=true]:before,.sidebar .full-list>li>ul>li>a:hover:before{content:"\2022";position:absolute;margin-left:-15px;color:var(--sidebarActiveItem)}.sidebar .full-list ul li{line-height:var(--sidebarFontSize);padding:0 8px}.sidebar .full-list ul a{padding-left:15px;height:24px}.sidebar .full-list ul button{font-size:var(--sidebarFontSize)}.sidebar .full-list ul button:after{content:var(--icon-add)}.sidebar .full-list ul button[aria-expanded=true]:after{content:var(--icon-subtract)}.sidebar .full-list ul ul{margin:9px 0 9px 10px}.sidebar .full-list ul ul li{height:20px;color:var(--sidebarAccentMain)}.sidebar .full-list ul ul a{border-left:1px solid var(--sidebarInactiveItemMarker);padding:0 10px;height:20px}.sidebar .full-list ul ul a:hover{border-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list ul ul a[aria-selected]{color:var(--sidebarActiveItem);border-color:var(--sidebarLanguageAccentBar)}.sidebar-button{cursor:pointer;background-color:transparent;border:none;font-size:var(--sidebarFontSize);color:var(--sidebarAccentMain)}.sidebar-button:hover{color:var(--sidebarHover)}body:not(.sidebar-open) .sidebar-button{color:var(--contrast)}@media screen and (max-height:500px){.sidebar{overflow-y:auto}.sidebar .full-list{overflow:visible}}.top-search{background-color:var(--background);top:0;z-index:99;position:relative;width:100%;padding:10px 0}.search-settings{display:flex;column-gap:12px;align-items:center;width:100%;position:relative}.search-bar{border:1px solid var(--searchBarBorder);border-radius:var(--borderRadius-base);height:40px;position:relative;width:100%}.top-search .search-bar .search-input{background-color:var(--searchSearch);border:1px solid transparent;border-radius:var(--borderRadius-base);color:var(--searchAccentMain);position:relative;height:38px;padding:8px 40px 8px 12px;width:100%;transition:var(--transition-all)}.top-search .search-bar .search-input::placeholder{color:var(--searchAccentMain);opacity:.5}.top-search .search-bar .search-input:focus{border:1px solid var(--searchBarFocusColor);border-radius:calc(var(--borderRadius-base) - 1px);position:relative}.top-search .search-bar .search-label{position:relative}.top-search .search-bar .search-input-wrapper{position:relative;display:flex;align-items:center}.top-search .search-settings .engine-selector{position:relative}.top-search .search-settings .engine-selector[data-multiple=false]{display:none}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button{cursor:default}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button .ri-arrow-down-s-line{display:none}.top-search .search-settings .engine-button{display:flex;align-items:center;gap:6px;background-color:var(--autocompleteHover);border:none;color:var(--searchAccentMain);cursor:pointer;font-size:var(--text-sm);padding:8px 12px;border-radius:var(--borderRadius-base);transition:var(--transition-all);height:40px}.top-search .search-settings .engine-button:hover,.top-search .search-settings .engine-button[aria-expanded=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-name{font-size:var(--text-sm);white-space:nowrap}.top-search .search-settings .engine-dropdown{position:absolute;top:calc(100% + 10px);right:0;min-width:200px;max-width:50vh;width:max-content;background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;z-index:200}.top-search .search-settings .engine-dropdown[hidden]{display:none}.top-search .search-settings .engine-option{display:block;width:100%;text-align:left;background-color:transparent;border:0;color:var(--textHeaders);cursor:pointer;padding:10px 12px;transition:var(--transition-colors)}.top-search .search-settings .engine-option:hover{background-color:var(--autocompleteHover)}.top-search .search-settings .engine-option:focus{border-left:2px solid var(--main);padding-left:10px;background-color:var(--autocompleteHover);outline:none}.top-search .search-settings .engine-option[aria-checked=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-option .name{display:block;font-size:var(--text-sm);font-weight:500;margin-bottom:2px}.top-search .search-settings .engine-option .help{display:block;font-size:var(--text-xs);opacity:.7}.top-search .search-bar .search-close-button{font-size:var(--text-md);color:var(--searchAccentMain);background-color:transparent;border:none;cursor:pointer;margin:0;opacity:.5;padding:5px;transform:scaleY(0);transition:var(--transition-all);display:flex;align-items:center;position:absolute;right:0;top:50%;transform:translateY(-50%) scaleY(0)}.top-search .search-bar .search-close-button:hover{opacity:.7}.top-search .search-settings button.icon-settings{display:flex;align-items:center;justify-content:flex-end}.top-search .search-settings .icon-settings{font-size:var(--text-xl);float:right;color:var(--iconAction);text-decoration:none;border:none;transition:color .3s ease-in-out;background-color:transparent;cursor:pointer;padding:0}.top-search .search-settings .icon-settings:hover{color:var(--iconActionHover)}.top-search .search-settings .icon-settings:visited{color:var(--iconAction)}@media screen and (max-width:768px){.top-search{padding-left:calc(var(--content-gutter) + 36px);padding-right:var(--content-gutter);margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));width:calc(2 * var(--content-gutter) + 100%)}.search-settings{width:100%;box-sizing:border-box}.top-search .search-settings .engine-name{display:none}}body.search-focused .search-bar .search-close-button{transform:translateY(-50%) scaleY(1);transition:var(--transition-all)}@media screen and (hover:hover){body.search-focused .top-search{position:sticky!important}body.search-focused .sidebar-button{position:fixed!important}}@media screen and (hover:none){body.scroll-sticky .top-search{position:sticky!important}body.scroll-sticky .sidebar-button{position:fixed!important}}*:focus,button:focus,[type=button]:focus,[type=reset]:focus,[type=submit]:focus{outline:2px solid var(--main);outline-offset:-2px}*:focus:not(:focus-visible),button:focus:not(:focus-visible),[type=button]:focus:not(:focus-visible),[type=reset]:focus:not(:focus-visible),[type=submit]:focus:not(:focus-visible){outline:0}input[type=text],input[type=number],input[type=date],input[type=datetime],input[type=datetime-local],input[type=email],input[type=month],input[type=password],input[type=search],input[type=tel],input[type=time],input[type=url],input[type=week],textarea{outline:0}.content-inner{font-size:1em;line-height:1.6875em;position:relative;background-color:var(--background);color:var(--textBody)}.content-inner .heading-with-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px}.content-inner .heading-with-actions>*:not(h1){flex-shrink:0}.content-inner .heading-with-actions h1{flex-grow:1;justify-self:flex-start;max-width:100%;margin:0;overflow-wrap:break-word}.content-inner .heading-with-actions .icon-action{width:20px;height:20px;display:flex;justify-content:center;align-items:center;font-weight:400;margin-left:3px}.content-inner .heading-with-actions.top-heading .icon-action{font-size:1.2rem}@container content (width > 600px){.content-inner .heading-with-actions.top-heading{flex-wrap:nowrap;align-items:flex-start;& h1{padding-right:32px}.icon-action{padding-top:1.7rem}}}.content-inner .top-heading{padding-top:1rem}.content-inner :is(h1,h2,h3,h4,h5,h6){font-family:var(--sansFontFamily);font-weight:700;line-height:1.5em;word-wrap:break-word;color:var(--textHeaders)}.content-inner div.group-description{margin:0 0 3em}.content-inner h1 small{font-weight:400}.content-inner{& h1{font-size:1.5rem;margin-top:1.75em}& h2{font-size:1.35rem;margin-top:1.5em;margin-bottom:.5em}& h3{font-size:1.25rem;margin-top:1.5em;margin-bottom:.5em}:is(h4,h5,h6){font-size:1.15rem;margin-top:1.25em;margin-bottom:.5em}:is(h5,h6){font-weight:400}& h6{font-size:1rem}}@container content (width > 600px){.content-inner{& h1{font-size:2rem}& h2{font-size:1.75rem}& h3{font-size:1.45rem}}}.content-inner li+li{margin-top:.25em}.content-inner :is(a,.a-main){color:var(--link-color);text-decoration:underline;text-decoration-skip-ink:auto}.content-inner :is(a:visited,.a-main:visited){color:var(--link-visited-color)}.content-inner .icon-action{color:var(--iconAction);text-decoration:none;border:none;transition:var(--transition-colors);background-color:transparent;cursor:pointer}.content-inner .icon-action:hover{color:var(--iconActionHover)}.content-inner .icon-action:visited{color:var(--iconAction)}.content-inner .livebook-badge-container{display:flex}.content-inner a.livebook-badge{display:inline-flex}.content-inner .note{color:var(--iconAction);font-size:var(--text-xs);font-weight:400}.content-inner{:is(blockquote,section.admonition){position:relative;margin:1.5625em 0;padding:0 1.2rem;overflow:auto;border-radius:var(--borderRadius-base);:is(h1,h2,h3,h4,h5,h6){margin:1.25rem 0}:is(h1,h2,h3){font-size:1.375rem}:is(h4,h5,h6){font-size:1rem}:is(code,pre code){background-color:var(--admCodeBackground);border:1px solid var(--admCodeBorder);color:var(--admCodeColor)}:is(a,a:visited){color:inherit}:is(h1,h2,h3,h4,h5,h6):first-child{padding-bottom:1rem;border-bottom:2px solid hsl(from currentColor h s l / 15%)}& p:last-child{padding-bottom:1em;margin-bottom:0}}& blockquote{border-left:3px solid var(--blockquoteBorder);background-color:var(--blockquoteBackground)}}@media print{.content-inner :is(blockquote,section.admonition){border:2px solid hsl(from var(--textBody) h s l / 15%);:is(code,pre code){border-color:var(--gray400)}& pre code.makeup{white-space:break-spaces;break-inside:avoid}}}.content-inner table{margin:2em 0;border-collapse:collapse;display:block;overflow:auto}.content-inner th{text-align:left;font-family:var(--sansFontFamily);font-weight:700;padding-bottom:.5em;white-space:nowrap}.content-inner thead tr{border-bottom:1px solid var(--tableHeadBorder)}.content-inner tbody tr{border-bottom:1px solid var(--tableBodyBorder)}.content-inner tbody tr:last-child{border-bottom:none}.content-inner tr{vertical-align:bottom;height:2.5em}.content-inner :is(td,th){padding:.25em .25em .25em 1em;line-height:2em;vertical-align:top}.content-inner .section-heading{--icon-size: 16px;--icon-spacing: 5px;display:grid;grid-template:1fr / 1fr}@media screen and (max-width:768px){.content-inner .section-heading{--icon-spacing: 2px}}.content-inner .section-heading>:is(.hover-link,.text){grid-row:1;grid-column:1}.content-inner .section-heading .hover-link{text-decoration:none}.content-inner .section-heading i{font-size:var(--icon-size);color:var(--mainLight);top:-2px;margin-left:calc(-1 * (var(--icon-size) + var(--icon-spacing)));padding-right:var(--icon-spacing);position:relative;opacity:0}.content-inner :is(blockquote,section.admonition) .section-heading i{display:none}.content-inner .section-heading:is(:hover,:focus,:target) i{opacity:1}.content-inner .app-vsn{display:none!important;font-size:.6em;line-height:1.5em}@media screen and (max-width:768px){.content-inner .app-vsn{display:block!important}}.content-inner img{max-width:100%}@media screen and (max-width:768px){.content-inner :is(ol,ul){padding-left:calc(1.5 * var(--content-gutter))}}.content-inner section.admonition{border-radius:var(--borderRadius-base)}.content-inner section.admonition>.admonition-title{color:var(--contrast);padding-left:2.2rem;font-weight:700;font-style:normal}.content-inner section.admonition>.admonition-title:before{color:var(--contrast);position:absolute;left:1rem;font-size:1.8rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.content-inner section.admonition{&.warning{background-color:var(--warningBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--warningHeading)}}}&.error{background-color:var(--errorBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--errorHeading)}}}&.info{background-color:var(--infoBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--infoHeading)}}}&.neutral{background-color:var(--neutralBackground);.admonition-title{&,&:before{content:var(--icon-double-quotes-l);color:var(--neutralHeading)}}}&.tip{background-color:var(--tipBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--tipHeading)}}}}@media print{.content-inner section.admonition{&.warning{border-color:hsl(from var(--warningHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--warningHue),90%,40%)}}}&.error{border-color:hsl(from var(--errorHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--errorHue),80%,40%)}}}&.info{border-color:hsl(from var(--infoHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--infoHue),80%,40%)}}}&.neutral{border-color:hsl(from var(--neutralHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--neutralHue),30%,40%)}}}&.tip{border-color:hsl(from var(--tipHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--tipHue),50%,40%)}}}}}.content-inner section.admonition>.admonition-title code{margin:0 .5ch}@media screen and (max-width:768px){.content-inner section.admonition{margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0}}.content-inner .summary h3 a{text-decoration:none;border:none;color:var(--textHeaders)!important}.content-inner .summary span.deprecated{color:var(--darkDeprecated);font-weight:400}.content-inner .summary .summary-row .summary-signature{font-family:var(--monoFontFamily);font-size:13px;font-weight:700}.content-inner .summary .summary-row .summary-signature a{text-decoration:none;border:none}.content-inner .summary .summary-row .summary-synopsis{padding:0 1.2em;margin:0 0 .5em}.content-inner .summary .summary-row .summary-synopsis p{margin:0;padding:0}@font-face{font-family:Consolas;src:local("Consolas");size-adjust:110%}.content-inner.content-inner :is(a:has(code,img),pre a){color:var(--link-color);text-shadow:none;text-decoration:none;background-image:none}.content-inner.content-inner :is(a:has(code,img),pre a):is(:visited,:active,:focus,:hover){color:var(--link-visited-color)}.content-inner strong>code{font-weight:700}.content-inner code{font-family:var(--monoFontFamily);font-style:normal;line-height:24px;font-weight:400;font-size:var(--text-sm);background-color:var(--codeBackground);vertical-align:baseline;border-radius:var(--borderRadius-sm);padding:.1em .2em;border:1px solid var(--codeBorder);text-transform:none}.content-inner code.inline{border-radius:var(--borderRadius-sm);word-wrap:break-word}.content-inner :is(h1,h2,h3,h4,h5,h6) code{font-size:.875em}.content-inner pre{margin:var(--baseLineHeight) 0}.content-inner pre code{display:block;overflow-x:auto;white-space:inherit;padding:1em;scrollbar-width:thin}.content-inner pre code.output{margin:0 12px;max-height:400px;overflow:auto}.content-inner pre code.output+.copy-button{margin-right:12px}.content-inner pre code.output:before{content:"Output";display:block;position:absolute;top:-16px;left:12px;padding:2px 4px;font-size:var(--text-xs);font-family:var(--monoFontFamily);line-height:1;color:var(--textHeaders);background-color:var(--codeBackground);border:1px solid var(--codeBorder);border-bottom:0;border-radius:2px}@media screen and (max-width:768px){.content-inner>pre:has(code),.content-inner section>pre:has(code){margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter))}.content-inner>pre code,.content-inner section>pre code{padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0;border-left-width:0;border-right-width:0}}@keyframes blink-background{0%,to{background-color:var(--textDetailBackground)}50%{background-color:var(--blink)}}.content-inner .detail:target .detail-header{animation-duration:.55s;animation-name:blink-background;animation-iteration-count:1;animation-timing-function:ease-in-out}.content-inner .detail-header{margin:1em 0;padding:.5em .85em .5em 1em;background-color:var(--textDetailBackground);border-left:3px solid var(--textDetailAccent);font-size:1em;font-family:var(--monoFontFamily);position:relative}.content-inner .detail-header .signature{font-family:var(--monoFontFamily);font-weight:700;font-size:13px!important;line-height:2em!important;margin:0!important}.content-inner .detail-header:hover a.detail-link,.content-inner .detail-header a.detail-link:focus{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:var(--transition-opacity);position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;text-decoration:none;border:none}@media screen and (max-width:768px){.content-inner .detail-header a.detail-link{margin-left:-30px}}.content-inner .specs pre{font-family:var(--monoFontFamily);font-size:var(--text-xs);font-style:normal;line-height:24px;white-space:pre-wrap;margin:0;padding:0}.content-inner .specs .attribute{color:var(--fnSpecAttr)}.content-inner .docstring{margin:1.2em 0 3em 1.2em}@media screen and (max-width:768px){.content-inner .docstring{margin-left:0}}.content-inner .docstring{:not(.admonition-title){&:is(h1,h2,h3,h4,h5,h6){font-weight:700;font-size:1em;margin-top:2em}&:is(h1,h2){font-size:1.25em}&:is(h3){font-size:1.1em}}}.content-inner div.deprecated{display:block;padding:1em;background-color:var(--fnDeprecated);border-radius:var(--borderRadius-sm);margin:var(--baseLineHeight) 0}.content-inner .footer{margin:4em auto 1em;text-align:center;font-size:var(--text-sm)}.content-inner .footer .line{display:inline-block}.content-inner .footer .line a,.content-inner .footer .line button{padding:0 4px}.content-inner .footer button{background-color:transparent;border:0;cursor:pointer}.content-inner .bottom-actions{display:flex;justify-content:space-between;margin-top:7em;gap:12px}.bottom-actions-item{flex:1 1 0%}.content-inner .bottom-actions .bottom-actions-button{display:flex;text-decoration:none;flex-direction:column;border-radius:var(--borderRadius-sm);border:1px solid var(--bottomActionsBtnBorder);padding:12px 16px;min-width:150px;transition:var(--transition-all)}.content-inner .bottom-actions .bottom-actions-button:hover{border-color:var(--mainLight)}.content-inner .bottom-actions .bottom-actions-button .subheader{font-size:.8em;color:var(--textHeaders);white-space:nowrap}.content-inner .bottom-actions .bottom-actions-button .title{color:var(--bottomActionsBtnTitle)}.content-inner .bottom-actions .bottom-actions-button[rel=prev]{text-align:start}.content-inner .bottom-actions .bottom-actions-button[rel=next]{text-align:end}@media screen and (max-width:768px){.content-inner .bottom-actions{flex-direction:column-reverse}}.page-cheatmd .content-inner{--horizontal-space: 1.5em;--vertical-space: 1em}@media(max-width:600px){.page-cheatmd .content-inner{--horizontal-space: 1em;--vertical-space: .75em}}.page-cheatmd .content-inner{max-width:1200px}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& h1{margin-bottom:var(--vertical-space)}& h2{font-size:1.6rem;margin:var(--vertical-space) 0;column-span:all;color:var(--gray700);font-weight:500}& h3{font-size:1.375rem;margin:0 0 1em;font-weight:400}& section.h3{min-width:300px;margin:0;padding:0 0 calc(var(--vertical-space) * 2) 0;break-inside:avoid}& h3 .text{overflow:hidden}& h3 .text:after{content:"";margin-left:calc(var(--horizontal-space) / 2);vertical-align:baseline;display:inline-block;width:100%;height:1px;margin-right:-100%;margin-bottom:5px;background-color:var(--codeBorder)}& h4{font-size:1rem;display:block;margin:0;padding:.25em var(--horizontal-space);font-weight:400;background:var(--gray100);color:#567;border:solid 1px 1px 0 1px var(--gray100)}.h2 p{margin:0;display:block;background:var(--gray50);padding:var(--vertical-space) var(--horizontal-space)}.h2 p>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}& pre code{padding:var(--vertical-space) var(--horizontal-space)}.h2 pre{margin:0}.h2 pre+pre{margin-top:-1px}& pre.wrap{white-space:break-spaces}@media screen and (max-width:768px){& pre code{border-left-width:1px!important;border-right-width:1px!important}}.h2 table{display:table;box-sizing:border-box;width:100%;border-collapse:collapse;margin:0}.h2 th{padding:var(--vertical-space) var(--horizontal-space);line-height:inherit;margin-bottom:-1px;vertical-align:middle;border-bottom:1px solid var(--codeBorder)}.h2 td{padding:var(--vertical-space) var(--horizontal-space);border:0;border-bottom:1px solid var(--codeBorder)}.h2 tr:first-child{border-top:1px solid var(--codeBorder)}.h2 td code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}.h2 thead{background-color:var(--gray50)}.h2 tbody{background-color:var(--codeBackground)}.h2 :is(ul,ol){margin:0;padding:0}.h2 li{list-style-position:inside;padding:.5em var(--horizontal-space);line-height:2em;vertical-align:middle;background-color:var(--codeBackground);border-bottom:1px solid var(--codeBorder);margin-top:0}.h2 :is(ul,ol)+pre code{border-top:0}.h2 li>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}}}@scope(.dark .page-cheatmd)to (.admonition){:scope .content-inner{& h2{color:var(--gray200)}& h4{background:#192f50;color:var(--textBody);border:1px solid #192f50;border-bottom:0}.h2 p{background:var(--gray700)}.h2 thead{background-color:var(--gray700)}}}.page-cheatmd .content-inner{& section.width-50{display:block;width:50%;margin:0;>section>table{width:100%}}& section:is(.col-2,.col-2-left,.col-3){column-gap:40px}& section.col-2{column-count:2;height:auto}& section.col-2-left{display:grid;grid-template-columns:calc(100% / 3) auto;>h2{grid-column-end:span 2}}& section.col-3{column-count:3;height:auto}& section.list-4>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 25%}}& section.list-6>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 calc(100% / 6)}}@media screen and (max-width:1400px){& section.col-3{column-count:2}& section.col-2-left{display:flex;flex-direction:column}}@media screen and (max-width:1200px){& section:is(.col-2,.col-3){display:flex;flex-direction:column}& section.list-6>ul>li{flex:0 0 25%}}@media screen and (max-width:1000px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 calc(100% / 3)}}@media screen and (max-width:600px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 50%}& section.width-50{width:100%}}}@keyframes loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.page-search #search{min-height:200px;position:relative;.loading{height:64px;width:64px;position:absolute;top:50%;left:calc(50% - 32px)}.loading div{box-sizing:border-box;display:block;position:absolute;width:51px;height:51px;margin:6px;border:6px solid var(--coldGray);border-radius:50%;animation:loading 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:var(--coldGray) transparent transparent transparent}.loading div:nth-child(1){animation-delay:-.45s}.loading div:nth-child(2){animation-delay:-.3s}.loading div:nth-child(3){animation-delay:-.15s}.result{margin:2em 0}.result p{margin:0}.result-id{font-size:1.4em;margin:0}.result-id a{text-decoration:none;color:var(--textHeaders);transition:var(--transition-colors)}.result-id a:is(:visited,:active){color:var(--textHeaders)}.result-id a:is(:hover,:focus){color:var(--main)}:is(.result-id,.result-elem) em{font-style:normal;color:var(--main)}.result-id small{font-weight:400}}@keyframes keyboard-shortcuts-show{0%{opacity:0}to{opacity:1}}.modal{animation-duration:.15s;animation-name:keyboard-shortcuts-show;animation-iteration-count:1;animation-timing-function:ease-in-out;display:none;background-color:#000000bf;position:fixed;inset:0;z-index:300}.modal.shown{display:block}.modal .modal-contents{margin:75px auto 0;max-width:500px;background-color:var(--modalBackground);border-radius:var(--borderRadius-sm);box-shadow:2px 2px 8px #0003;padding:25px 35px 35px}@media screen and (max-width:768px){.modal .modal-contents{padding:20px}}.modal .modal-header{display:flex;align-items:start}.modal .modal-title{display:inline-block;flex-grow:1;font-size:1.2rem;font-weight:700;margin-bottom:20px}.modal .modal-title button{border:none;background-color:transparent;color:var(--textHeaders);font-weight:700;margin-right:30px;padding-left:0;text-align:left;transition:var(--transition-colors)}.modal .modal-title button:hover{color:var(--main);cursor:pointer}.modal .modal-title button.active{color:var(--main)}.modal .modal-close{cursor:pointer;display:block;font-size:1.5rem;margin:-8px -8px 0 0;padding:8px;opacity:.7;background-color:transparent;color:var(--textHeaders);border:none;transition:var(--transition-opacity)}.modal .modal-close:hover{opacity:1}#keyboard-shortcuts-content dl.shortcut-row{display:flex;align-items:center;justify-content:space-between;margin:0;padding:6px 0 8px;border-bottom:1px solid var(--settingsSectionBorder)}#keyboard-shortcuts-content dl.shortcut-row:last-of-type{border-bottom-style:none}#keyboard-shortcuts-content dl.shortcut-row:first-child{padding-top:0}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){display:inline-block}#keyboard-shortcuts-content kbd>kbd{background-color:var(--settingsInputBorder);color:var(--contrast);border-radius:var(--borderRadius-sm);font-family:inherit;font-weight:700;display:inline-block;line-height:1;padding:4px 7px 6px;min-width:26px;text-align:center;font-size:var(--text-sm)}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){margin:0}#quick-switch-modal-body{width:100%;position:relative}#quick-switch-modal-body .ri-search-2-line{position:absolute;left:0;top:0;padding:4px 10px;color:var(--quickSwitchContour);font-weight:700}#quick-switch-modal-body #quick-switch-input{width:100%;padding:8px 6px 8px 38px;border:none;color:var(--quickSwitchInput);background-color:transparent;border-bottom:1px solid var(--quickSwitchContour);box-sizing:border-box;transition:all .12s ease-out}#quick-switch-modal-body #quick-switch-results{margin:0}#quick-switch-modal-body .quick-switch-result{padding:2px 5px;border-bottom:1px dotted var(--quickSwitchContour);transition:all .12s ease-out}#quick-switch-modal-body .quick-switch-result:last-child{border-bottom:none}#quick-switch-modal-body .quick-switch-result:hover{cursor:pointer}#quick-switch-modal-body .quick-switch-result:is(:hover,.selected){border-left:4px solid var(--main);background-color:var(--codeBackground)}.autocomplete{display:none;position:absolute;width:calc(100% - 32px);top:55px}.autocomplete .triangle{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-bottom:12px solid var(--autocompleteBackground);position:absolute;top:8px;left:26px;transform:translate(-50%);z-index:100;background-color:transparent}.autocomplete-results{margin:0;padding:15px 20px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;color:var(--autocompleteResults);font-weight:300;font-size:.9rem;font-style:italic}.autocomplete-results .query{margin-right:auto}.autocomplete-results .bold{color:var(--autocompleteResultsBold);font-weight:400}.autocomplete.shown{display:block}.autocomplete-container{position:absolute;top:5px;width:100%;z-index:200}.autocomplete-suggestions{background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;overflow-y:auto;max-height:450px;white-space:normal;overflow-x:hidden;overscroll-behavior-y:contain;scrollbar-width:thin}.autocomplete-suggestion{color:var(--textHeaders)!important;display:block;padding:12px 20px;text-decoration:none!important;transition:var(--transition-colors);border-top:1px solid var(--suggestionBorder);font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.autocomplete-suggestion.selected{background-color:var(--autocompleteSelected)}.autocomplete-suggestion:hover{background-color:var(--autocompleteHover)}.autocomplete-suggestion em{font-style:normal;font-weight:700}.autocomplete-suggestion .separator{opacity:.4;margin:0 2px}.autocomplete-suggestion .description{opacity:.6}.autocomplete-suggestion .label{background-color:var(--autocompleteLabelBack);opacity:.6;color:var(--autocompleteLabelFont);padding:4px 8px;border-radius:4px;margin-left:5px;text-transform:uppercase;font-family:var(--sansFontFamily);font-size:.7rem}@media screen and (hover:none){.autocomplete-results .press-return{display:none!important}}.tooltip{box-shadow:0 0 10px var(--black-opacity-10);max-height:300px;max-width:500px;padding:0;position:absolute;pointer-events:none;margin:0;z-index:99;top:0;left:0;visibility:hidden;transform:translateY(20px);opacity:0;transition:.2s visibility ease-out,.2s transform ease-out,.2s opacity ease-out}.tooltip.tooltip-shown{visibility:visible;transform:translateY(0);opacity:1}.tooltip .tooltip-body{border:1px solid var(--codeBorder);border-radius:var(--borderRadius-sm);overflow:auto}.tooltip .tooltip-body .signature{min-width:320px;width:100%;line-height:1em!important;margin:.75em 0!important}.tooltip .tooltip-body .detail-header{border-left:0;margin-bottom:0;margin-top:0}.tooltip .tooltip-body .docstring{background-color:var(--background);padding:1.2em;margin:0;width:498px}.tooltip .tooltip-body .docstring-plain{max-width:498px;width:auto}.tooltip .tooltip-body .version-info{float:right;font-family:var(--monoFontFamily);font-weight:400;opacity:.3;padding-left:.3em}pre{position:relative}pre:hover .copy-button,pre .copy-button:focus{opacity:1}.copy-button{display:flex;opacity:0;position:absolute;top:7px;right:8px;padding:8px;background-color:transparent;backdrop-filter:blur(8px);border-radius:var(--borderRadius-sm);border:1px solid var(--codeBorder);cursor:pointer;transition:var(--transition-all);font-size:var(--text-sm);line-height:24px;color:currentColor;& svg[aria-live=polite]{display:none}}.copy-button svg{opacity:.5;transition:var(--transition-all)}pre .copy-button:hover svg,pre .copy-button:focus-visible svg{opacity:1}.copy-button svg{width:20px}.copy-button.clicked{opacity:1;color:var(--success);& svg[aria-live=polite]{display:block}}.copy-button.clicked svg{display:none;color:currentColor}#settings-modal-content{margin-top:10px}#settings-modal-content .hidden{display:none}#settings-modal-content .input{box-sizing:border-box;width:80%;padding:8px;font-size:var(--text-sm);background-color:var(--settingsInputBackground);color:var(--settingsInput);border:1px solid var(--settingsInputBorder);border-radius:var(--borderRadius-base);transition:var(--transition-all)}#settings-modal-content .input:focus{border-color:var(--main)}#settings-modal-content .input::placeholder{color:var(--gray400)}#settings-modal-content .switch-button-container{display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--settingsSectionBorder);padding:10px 0}#settings-modal-content .switch-button-container:first-of-type{border-top-style:none;padding-top:0}#settings-modal-content .switch-button-container>div>span{font-size:var(--text-md)}#settings-modal-content .switch-button-container>div>p{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:var(--text-sm);line-height:1.4;margin:0;padding-bottom:6px;padding-right:10px}#settings-modal-content .switch-button{position:relative;display:inline-block;flex-shrink:0;width:40px;height:20px;user-select:none;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox{appearance:none;position:absolute;display:block;width:20px;height:20px;border-radius:1000px;background-color:#91a4b7;border:3px solid #e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__bg{display:block;width:100%;height:100%;border-radius:1000px;background-color:#e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox:checked{background-color:#fff;border-color:var(--main);transform:translate(100%)}#settings-modal-content .switch-button__checkbox:checked+.switch-button__bg{background-color:var(--main)}#settings-modal-content .switch-button__checkbox:focus{outline:0}#settings-modal-content .switch-button__checkbox:focus+.switch-button__bg{outline:2px solid var(--main);outline-offset:2px}#settings-modal-content .switch-button__checkbox:focus:not(:focus-visible)+.switch-button__bg{outline:0}#settings-modal-content .settings-select{cursor:pointer;position:relative;border:none;background-color:transparent;color:var(--textBody)}#settings-modal-content .settings-select option{color:initial}#toast{visibility:hidden;opacity:0;position:fixed;z-index:1000;right:10px;top:0;font-size:.9rem;min-width:3rem;padding:.7rem 1.2rem .7rem 2.8rem;text-align:left;font-weight:400;border-radius:var(--borderRadius-base);border:1px solid hsl(from var(--tipHeading) h s l / 15%);background-color:var(--tipBackground);color:var(--tipHeading);transition:opacity .4s ease-in-out,transform .3s ease-out;cursor:default}#toast:before{content:var(--icon-information);position:absolute;left:1rem;font-size:1.2rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#toast.show{visibility:visible;opacity:1;transform:translateY(.75rem)}@media(prefers-reduced-motion:reduce){#toast{transition:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;user-select:none}@media print{.body-wrapper{display:block}.sidebar,.sidebar-button,.top-search{display:none}.content{padding-left:0;overflow:visible;left:0;width:100%}.summary-row{break-inside:avoid}#toast{display:none}.content-inner{padding:0}.content-inner .section-heading a.hover-link,.content-inner button.icon-action,.content-inner a.icon-action,.content-inner .bottom-actions{display:none}.footer p:first-of-type{display:none}.content-inner pre code.makeup{white-space:break-spaces;break-inside:avoid}}@media print{.page-cheatmd .content-inner{max-width:100%;width:100%;padding:0;font-size:.7em;& section:is(.col-2,.col-2-left,.col-3){column-gap:30px}& section.col-2{column-count:2}& section.col-2-left{display:grid}& section.col-3{column-count:3}}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& *{background-color:transparent!important;border-color:var(--gray400)!important}& h1{margin-top:0;margin-bottom:.5em}& h2.section-heading{font-weight:700;margin-top:1em;column-span:all}& section.h2{break-inside:avoid}& h3{font-weight:700;color:var(--mainDark)}& h3:after{height:2px;background-color:var(--gray400)}& section.h3{min-width:300px;break-inside:avoid}& h4{padding:.5em 0;border:none;font-weight:700;color:#000}.h2 p{padding-left:0;padding-right:0;border:none!important}& code{line-height:1.5em}.h2 table{font-variant-numeric:tabular-nums;break-inside:avoid}.h2 :is(th,td){vertical-align:top;padding-left:0;padding-right:0}.h2 thead{border-style:solid none;border-width:1px}.h2 tr{border-bottom:none}.h2 th{font-weight:700}.h2 li{padding-left:0;padding-right:0;vertical-align:middle;border-bottom:none}& pre:hover button.copy-button{display:none}& div.tooltip{display:none}& footer p:not(.built-using){display:none}}}}code.makeup .unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.makeup .hll{background-color:#ffc}.makeup .bp{color:#3465a4}.makeup .c,.makeup .c1,.makeup .ch,.makeup .cm,.makeup .cp,.makeup .cpf,.makeup .cs{color:#4d4d4d}.makeup .dl{color:#408200}.makeup .err{color:#a40000;border:#ef2929}.makeup .fm,.makeup .g{color:#4d4d4c}.makeup .gd{color:#a40000}.makeup .ge{color:#4d4d4c;font-style:italic}.makeup .gh{color:navy;font-weight:700}.makeup .gi{color:#00a000}.makeup .go{color:#4d4d4c;font-style:italic}.makeup .gp{color:#4d4d4d}.makeup .gr{color:#ef2929}.makeup .gs{color:#4d4d4c;font-weight:700}.makeup .gt{color:#a40000;font-weight:700}.makeup .gu{color:purple;font-weight:700}.makeup .il{color:#0000cf;font-weight:700}.makeup .k,.makeup .kc,.makeup .kd,.makeup .kn,.makeup .kp,.makeup .kr,.makeup .kt{color:#204a87}.makeup .l{color:#4d4d4c}.makeup .ld{color:#c00}.makeup .m,.makeup .mb,.makeup .mf,.makeup .mh,.makeup .mi,.makeup .mo{color:#2937ab}.makeup .n{color:#4d4d4c}.makeup .na{color:#8a7000}.makeup .nb{color:#204a87}.makeup .nc{color:#0000cf}.makeup .nd{color:#5c35cc;font-weight:700}.makeup .ne{color:#c00;font-weight:700}.makeup .nf{color:#b65800}.makeup .ni{color:#bc5400}.makeup .nl{color:#b65800}.makeup .nn{color:#4d4d4c}.makeup .no{color:#a06600}.makeup .nt{color:#204a87;font-weight:700}.makeup .nv,.makeup .nx{color:#4d4d4c}.makeup .o{color:#bc5400}.makeup .ow{color:#204a87}.makeup .p,.makeup .py{color:#4d4d4c}.makeup .s,.makeup .s1,.makeup .s2,.makeup .sa,.makeup .sb,.makeup .sc{color:#408200}.makeup .sd{color:#8f5902;font-style:italic}.makeup .se{color:#204a87}.makeup .sh{color:#408200}.makeup .si{color:#204a87}.makeup .sr{color:#c00}.makeup .ss{color:#a06600}.makeup .sx{color:#408200}.makeup .vc,.makeup .vg,.makeup .vi,.makeup .vm,.makeup .x{color:#4d4d4c}.dark .makeup{color:#dce1e6}.dark .makeup .hll{background-color:#49483e}.dark .makeup .bp{color:#dce1e6}.dark .makeup .c,.dark .makeup .c1,.dark .makeup .ch,.dark .makeup .cm,.dark .makeup .cp,.dark .makeup .cpf,.dark .makeup .cs{color:#969386}.dark .makeup .dl{color:#e6db74}.dark .makeup .err{color:#960050;background-color:#1e0010}.dark .makeup .fm{color:#a6e22e}.dark .makeup .gd{color:#ff5385}.dark .makeup .ge{font-style:italic}.dark .makeup .gi{color:#a6e22e}.dark .makeup .gp{color:#969386}.dark .makeup .gs{font-weight:700}.dark .makeup .gu{color:#969386}.dark .makeup .gt{color:#ff5385;font-weight:700}.dark .makeup .il{color:#ae81ff}.dark .makeup .k,.dark .makeup .kc,.dark .makeup .kd{color:#66d9ef}.dark .makeup .kn{color:#ff5385}.dark .makeup .kp,.dark .makeup .kr,.dark .makeup .kt{color:#66d9ef}.dark .makeup .l,.dark .makeup .ld,.dark .makeup .m,.dark .makeup .mb,.dark .makeup .mf,.dark .makeup .mh,.dark .makeup .mi,.dark .makeup .mo{color:#ae81ff}.dark .makeup .n{color:#dce1e6}.dark .makeup .na{color:#a6e22e}.dark .makeup .nb{color:#dce1e6}.dark .makeup .nc,.dark .makeup .nd,.dark .makeup .ne,.dark .makeup .nf{color:#a6e22e}.dark .makeup .ni,.dark .makeup .nl,.dark .makeup .nn{color:#dce1e6}.dark .makeup .no{color:#66d9ef}.dark .makeup .nt{color:#ff5385}.dark .makeup .nv{color:#dce1e6}.dark .makeup .nx{color:#a6e22e}.dark .makeup .o,.dark .makeup .ow{color:#ff5385}.dark .makeup .p,.dark .makeup .py{color:#dce1e6}.dark .makeup .s,.dark .makeup .s1,.dark .makeup .s2,.dark .makeup .sa,.dark .makeup .sb,.dark .makeup .sc,.dark .makeup .sd{color:#e6db74}.dark .makeup .se{color:#ae81ff}.dark .makeup .sh,.dark .makeup .si,.dark .makeup .sr,.dark .makeup .ss,.dark .makeup .sx{color:#e6db74}.dark .makeup .vc,.dark .makeup .vg,.dark .makeup .vi,.dark .makeup .vm{color:#dce1e6}.tabset{--borderWidth: 1px;--tabsetPadding: var(--baseLineHeight);margin:var(--baseLineHeight) 0;border:var(--borderWidth) solid var(--tabBorder);padding:0 var(--tabsetPadding);border-radius:var(--borderRadius-lg)}.tabset-tablist{display:flex;overflow:auto;scrollbar-width:thin;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--tabBorderTop)}.tabset-tab{padding:1.1rem var(--tabsetPadding);font-family:var(--sansFontFamily);color:var(--textColor);margin-right:calc(-1 * var(--borderWidth));background-color:transparent;border:0;box-shadow:none;cursor:pointer;border-bottom-width:2px;border-bottom-style:solid;border-bottom-color:transparent;transition:var(--transition-all)}:hover.tabset-tab{border-bottom-color:var(--tabBorderTop);color:var(--textHeaders)}.tabset-tab[aria-selected=true]{border-bottom-color:var(--mainLight);color:var(--textHeaders)}.tabset-tab[aria-selected=true]:focus-visible{background-color:var(--mainLight);border-color:var(--mainLight);color:var(--white)}@media screen and (max-width:768px){.tabset{--tabsetPadding: calc(var(--baseLineHeight) / 2)}.tabset-panel{padding-top:calc(var(--tabsetPadding) / 2);padding-bottom:calc(var(--tabsetPadding) / 2)}.tabset-panel pre,.tabset-panel blockquote,.tabset-panel section.admonition{margin-left:calc(-1 * var(--tabsetPadding))!important;margin-right:calc(-1 * var(--tabsetPadding))!important}.tabset-panel>pre code{border-left-width:0;border-right-width:0}}@media screen and (max-width:768px){.tabset-panel>:is(:first-child){&:is(table){margin:.5em 0}}}@media screen and (min-width:769px){.tabset-panel>:is(:first-child){&:is(blockquote,.admonition){margin-top:1.5em}&:is(p:has(img)){margin-top:1.25em}&:is(table){margin-top:.75em}}.tabset-panel>:is(:last-child){&:is(blockquote,.admonition){margin-bottom:1.5em}&:is(p:not(:has(img)),ul,ol){margin-bottom:1.25em}&:is(table){margin-bottom:.75em}}}body:not(.dark) .content-inner img[src*="#gh-dark-mode-only"],body.dark .content-inner img[src*="#gh-light-mode-only"]{display:none} +/*! Bundled license information: + +modern-normalize/modern-normalize.css: + (*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize *) +*/ diff --git a/doc/dist/lato-latin-400-normal-W7754I4D.woff2 b/doc/dist/lato-latin-400-normal-W7754I4D.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ff60934dd0ea9cdd93f961d2dfd1e158357f2a77 GIT binary patch literal 23580 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 QOFh<%JiLFgmWQ+r07g396aWAK literal 0 HcmV?d00001 diff --git a/doc/dist/lato-latin-700-normal-2XVSBPG4.woff2 b/doc/dist/lato-latin-700-normal-2XVSBPG4.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..11de83feaf0fb872c91fce89534fd4e2d8bbda28 GIT binary patch literal 23040 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 literal 0 HcmV?d00001 diff --git a/doc/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 b/doc/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..edb9fa6f492362a9ab1f1d674b8f6e6f4e2085d0 GIT binary patch literal 5472 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 WhF)K4IGNT+hd8;|K8&%*Sz!P*WW1>W literal 0 HcmV?d00001 diff --git a/doc/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 b/doc/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2c8aaa866eee8bee09ee05a9809c2fda7cd87bd1 GIT binary patch literal 5368 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 literal 0 HcmV?d00001 diff --git a/doc/dist/search_data-85BCB47E.js b/doc/dist/search_data-85BCB47E.js new file mode 100644 index 0000000..a6e9abe --- /dev/null +++ b/doc/dist/search_data-85BCB47E.js @@ -0,0 +1 @@ +searchData={"items":[{"type":"module","title":"nova_liveboard","doc":"Configuration helpers for the liveboard.\n\nAll settings live under the `nova_liveboard` application env:\n\n- `prefix` (default `\"/liveboard\"`) - the path the dashboard mounts on.\n- `refresh_ms` (default `2000`) - how often the polled SSE streams repaint.\n- `request_buffer` (default `200`) - how many recent requests the tracer keeps.","ref":"nova_liveboard.html"},{"type":"function","title":"nova_liveboard.prefix/0","doc":"The mount path, always as a binary with a leading slash.","ref":"nova_liveboard.html#prefix/0"},{"type":"function","title":"nova_liveboard.refresh_ms/0","doc":"Repaint interval for the polled (non-request) streams, in milliseconds.","ref":"nova_liveboard.html#refresh_ms/0"},{"type":"function","title":"nova_liveboard.request_buffer/0","doc":"How many completed requests the tracer ring buffer retains.","ref":"nova_liveboard.html#request_buffer/0"},{"type":"module","title":"nova_liveboard_action_controller","doc":"POST handlers for the Requests page controls (arm / disarm / clear deep trace).\n\nEach mutates `nova_liveboard_tracer` and returns a one-shot Datastar response:\na normal `{status, 200, ...}` reply whose body is SSE-framed patch events.\nNova's built-in `handle_status` sends it, and Datastar applies the patches to\nthe clicking client immediately. Other open clients converge via the tracer's\nown notifications to their Requests stream.","ref":"nova_liveboard_action_controller.html"},{"type":"function","title":"nova_liveboard_action_controller.clear/1","doc":"","ref":"nova_liveboard_action_controller.html#clear/1"},{"type":"function","title":"nova_liveboard_action_controller.trace_start/1","doc":"","ref":"nova_liveboard_action_controller.html#trace_start/1"},{"type":"function","title":"nova_liveboard_action_controller.trace_stop/1","doc":"","ref":"nova_liveboard_action_controller.html#trace_stop/1"},{"type":"module","title":"nova_liveboard_app","doc":"Application entry point.\n\nStarts the supervision tree (the request tracer) and registers the\n`{stream, ...}` Datastar SSE return-handler with Nova. The request-tracing\nplugin is *not* auto-registered: it is a global Nova plugin, so the host wires\nit into its own `{nova, [{plugins, ...}]}` config (see the README). The\ndashboard's pages work either way; only the Requests feed needs the plugin.","ref":"nova_liveboard_app.html"},{"type":"function","title":"nova_liveboard_app.start/2","doc":"","ref":"nova_liveboard_app.html#start/2"},{"type":"function","title":"nova_liveboard_app.stop/1","doc":"","ref":"nova_liveboard_app.html#stop/1"},{"type":"module","title":"nova_liveboard_data","doc":"","ref":"nova_liveboard_data.html"},{"type":"function","title":"nova_liveboard_data.collect_metrics/1","doc":"","ref":"nova_liveboard_data.html#collect_metrics/1"},{"type":"function","title":"nova_liveboard_data.ets_tables/0","doc":"","ref":"nova_liveboard_data.html#ets_tables/0"},{"type":"function","title":"nova_liveboard_data.format_bytes/1","doc":"","ref":"nova_liveboard_data.html#format_bytes/1"},{"type":"function","title":"nova_liveboard_data.format_number/1","doc":"","ref":"nova_liveboard_data.html#format_number/1"},{"type":"function","title":"nova_liveboard_data.format_uptime/1","doc":"","ref":"nova_liveboard_data.html#format_uptime/1"},{"type":"function","title":"nova_liveboard_data.kura_available/0","doc":"","ref":"nova_liveboard_data.html#kura_available/0"},{"type":"function","title":"nova_liveboard_data.kura_migration_status/1","doc":"","ref":"nova_liveboard_data.html#kura_migration_status/1"},{"type":"function","title":"nova_liveboard_data.kura_pool_stats/1","doc":"","ref":"nova_liveboard_data.html#kura_pool_stats/1"},{"type":"function","title":"nova_liveboard_data.kura_repo_info/1","doc":"","ref":"nova_liveboard_data.html#kura_repo_info/1"},{"type":"function","title":"nova_liveboard_data.kura_repos/0","doc":"","ref":"nova_liveboard_data.html#kura_repos/0"},{"type":"function","title":"nova_liveboard_data.kura_schemas/1","doc":"","ref":"nova_liveboard_data.html#kura_schemas/1"},{"type":"function","title":"nova_liveboard_data.port_info/0","doc":"","ref":"nova_liveboard_data.html#port_info/0"},{"type":"function","title":"nova_liveboard_data.running_applications/0","doc":"","ref":"nova_liveboard_data.html#running_applications/0"},{"type":"function","title":"nova_liveboard_data.scheduler_info/0","doc":"","ref":"nova_liveboard_data.html#scheduler_info/0"},{"type":"function","title":"nova_liveboard_data.sparkline_points/3","doc":"","ref":"nova_liveboard_data.html#sparkline_points/3"},{"type":"function","title":"nova_liveboard_data.supervision_tree/1","doc":"","ref":"nova_liveboard_data.html#supervision_tree/1"},{"type":"function","title":"nova_liveboard_data.system_info/0","doc":"","ref":"nova_liveboard_data.html#system_info/0"},{"type":"function","title":"nova_liveboard_data.top_processes/2","doc":"","ref":"nova_liveboard_data.html#top_processes/2"},{"type":"module","title":"nova_liveboard_html","doc":"Pure HTML rendering for the liveboard - \"BEAM mission control\".\n\nEvery function here takes already-gathered data (from `nova_liveboard_data`\nand `nova_liveboard_tracer`) and returns `iodata()`. No process state, no\nside effects beyond reading the static `nova_liveboard:prefix/0` config, so\nthe whole module is unit-testable in isolation.\n\n`page/4` renders the full document shell (vitals deck + instrument nav +\nmain stage). The per-region functions (`vitals_html/1`, `overview_html/1`,\n`processes_html/2`, ...) render the *inner* of a live region; the page\ncontroller uses them for the first paint and `nova_liveboard_sse` re-emits\nthem as Datastar patches as the VM changes.","ref":"nova_liveboard_html.html"},{"type":"function","title":"nova_liveboard_html.apps_html/1","doc":"Running applications table.","ref":"nova_liveboard_html.html#apps_html/1"},{"type":"function","title":"nova_liveboard_html.csp_headers/0","doc":"The dashboard's response headers. Strict CSP: everything is same-origin\n(self-hosted datastar.js, fonts, css), so any stray off-origin request fails\nloudly. `unsafe-eval` is required only for Datastar's `data-*` expression\nevaluation; the privacy-critical directives stay `'self'`.","ref":"nova_liveboard_html.html#csp_headers/0"},{"type":"function","title":"nova_liveboard_html.database_html/1","doc":"Kura repo pool panels.","ref":"nova_liveboard_html.html#database_html/1"},{"type":"function","title":"nova_liveboard_html.ets_html/1","doc":"ETS tables table.","ref":"nova_liveboard_html.html#ets_html/1"},{"type":"function","title":"nova_liveboard_html.html_escape/1","doc":"Escape `& < > \"` for safe interpolation into HTML.","ref":"nova_liveboard_html.html#html_escape/1"},{"type":"function","title":"nova_liveboard_html.metrics_html/1","doc":"Live sparklines for memory/IO/run-queue + scheduler utilisation bars.","ref":"nova_liveboard_html.html#metrics_html/1"},{"type":"function","title":"nova_liveboard_html.overview_html/1","doc":"System identity + memory breakdown + capacity panels.","ref":"nova_liveboard_html.html#overview_html/1"},{"type":"function","title":"nova_liveboard_html.page/4","doc":"Full HTML document: head + vitals deck + nav + the main stage.\n\n`StreamPath` is the SSE path the main region should subscribe to (so it\nrepaints live), or `none` for a static page (e.g. a request detail) that must\nnot be overwritten by a stream.","ref":"nova_liveboard_html.html#page/4"},{"type":"function","title":"nova_liveboard_html.ports_html/1","doc":"Open ports table.","ref":"nova_liveboard_html.html#ports_html/1"},{"type":"function","title":"nova_liveboard_html.processes_html/2","doc":"Top processes table, sorted by `SortBy`, with sort controls.","ref":"nova_liveboard_html.html#processes_html/2"},{"type":"function","title":"nova_liveboard_html.request_detail_html/1","doc":"Detail page for one captured request: timings + spawned-process tree.","ref":"nova_liveboard_html.html#request_detail_html/1"},{"type":"function","title":"nova_liveboard_html.request_row/1","doc":"A single request row (also emitted as a prepended SSE patch on arrival).","ref":"nova_liveboard_html.html#request_row/1"},{"type":"function","title":"nova_liveboard_html.requests_html/3","doc":"The live request feed. `Enabled` reflects whether the tracing plugin is\nregistered; when it is not, a setup hint is shown instead of an empty feed.\n`Trace` is the deep-trace arming state from `nova_liveboard_tracer`.","ref":"nova_liveboard_html.html#requests_html/3"},{"type":"function","title":"nova_liveboard_html.requests_toolbar_html/1","doc":"The arm/disarm/clear toolbar (patched on its own as trace state changes).","ref":"nova_liveboard_html.html#requests_toolbar_html/1"},{"type":"function","title":"nova_liveboard_html.schemas_html/1","doc":"Kura schema definitions (fields, associations, indexes).","ref":"nova_liveboard_html.html#schemas_html/1"},{"type":"function","title":"nova_liveboard_html.sup_tree_html/3","doc":"The supervision tree for `Active` (static process structure) + app chooser.","ref":"nova_liveboard_html.html#sup_tree_html/3"},{"type":"function","title":"nova_liveboard_html.vitals_html/1","doc":"The live header gauges: capacity vs limits + memory + uptime.","ref":"nova_liveboard_html.html#vitals_html/1"},{"type":"module","title":"nova_liveboard_page_controller","doc":"Nova controllers for the liveboard pages.\n\nEach page server-renders the full mission-control shell (`nova_liveboard_html:\npage/4`) with a first-paint snapshot, then a `data-init` stream takes over and\nrepaints the live region. `page_inner/2` is the single place that maps a page\nto its data + HTML; both these controllers and `nova_liveboard_sse` call it, so\nthe first paint and the live patches always agree.","ref":"nova_liveboard_page_controller.html"},{"type":"function","title":"nova_liveboard_page_controller.app_names/0","doc":"","ref":"nova_liveboard_page_controller.html#app_names/0"},{"type":"function","title":"nova_liveboard_page_controller.applications/1","doc":"","ref":"nova_liveboard_page_controller.html#applications/1"},{"type":"function","title":"nova_liveboard_page_controller.database/1","doc":"","ref":"nova_liveboard_page_controller.html#database/1"},{"type":"function","title":"nova_liveboard_page_controller.default_app/1","doc":"","ref":"nova_liveboard_page_controller.html#default_app/1"},{"type":"function","title":"nova_liveboard_page_controller.ets/1","doc":"","ref":"nova_liveboard_page_controller.html#ets/1"},{"type":"function","title":"nova_liveboard_page_controller.index/1","doc":"","ref":"nova_liveboard_page_controller.html#index/1"},{"type":"function","title":"nova_liveboard_page_controller.metrics/1","doc":"","ref":"nova_liveboard_page_controller.html#metrics/1"},{"type":"function","title":"nova_liveboard_page_controller.page_atom/1","doc":"Binary page name (from a route binding) to its page atom.","ref":"nova_liveboard_page_controller.html#page_atom/1"},{"type":"function","title":"nova_liveboard_page_controller.page_inner/2","doc":"Map a page to its freshly-gathered data, rendered to the live region inner.","ref":"nova_liveboard_page_controller.html#page_inner/2"},{"type":"function","title":"nova_liveboard_page_controller.ports/1","doc":"","ref":"nova_liveboard_page_controller.html#ports/1"},{"type":"function","title":"nova_liveboard_page_controller.processes/1","doc":"","ref":"nova_liveboard_page_controller.html#processes/1"},{"type":"function","title":"nova_liveboard_page_controller.request_show/1","doc":"","ref":"nova_liveboard_page_controller.html#request_show/1"},{"type":"function","title":"nova_liveboard_page_controller.requests/1","doc":"","ref":"nova_liveboard_page_controller.html#requests/1"},{"type":"function","title":"nova_liveboard_page_controller.schemas/1","doc":"","ref":"nova_liveboard_page_controller.html#schemas/1"},{"type":"function","title":"nova_liveboard_page_controller.supervisors/1","doc":"","ref":"nova_liveboard_page_controller.html#supervisors/1"},{"type":"module","title":"nova_liveboard_router","doc":"","ref":"nova_liveboard_router.html"},{"type":"function","title":"nova_liveboard_router.routes/1","doc":"","ref":"nova_liveboard_router.html#routes/1"},{"type":"module","title":"nova_liveboard_sse","doc":"The liveboard's live transport: one long-lived SSE stream per page region.\n\n`stream/1` is a normal Nova controller returning `{stream, Code, Headers,\nSource}`. That tuple is picked up by a return-handler we register\n(`handle_stream/3`), which `stream_reply`s the SSE headers and then **holds the\nconnection**, pushing `datastar:patch_elements/2` frames and never returning -\nso Nova's buffered `render_response` (which would reply again and crash) never\nruns. On client disconnect the next `stream_body` fails and the request process\nexits, dropping any tracer subscription. See novaframework/nova#387.\n\nTwo stream shapes:\n\n- **polled** (vitals/overview/processes/ets/applications/ports/supervisors/\n metrics): a `receive ... after RefreshMs` timer repaints the region. The\n metrics stream threads the `nova_liveboard_data:collect_metrics/1`\n accumulator through its loop so sparklines and scheduler deltas build up.\n- **event-driven** (requests): subscribes to `nova_liveboard_tracer` and\n prepends each request as it completes; no polling.\n\nThe handler is registered as an explicit `fun/3` because current Nova invokes\nreturn-handlers with 3 args, while its `{Mod, Fun}` form wraps to arity 4.","ref":"nova_liveboard_sse.html"},{"type":"function","title":"nova_liveboard_sse.handle_stream/3","doc":"","ref":"nova_liveboard_sse.html#handle_stream/3"},{"type":"function","title":"nova_liveboard_sse.register/0","doc":"","ref":"nova_liveboard_sse.html#register/0"},{"type":"function","title":"nova_liveboard_sse.stream/1","doc":"","ref":"nova_liveboard_sse.html#stream/1"},{"type":"module","title":"nova_liveboard_sup","doc":"Top supervisor: owns the request tracer.","ref":"nova_liveboard_sup.html"},{"type":"function","title":"nova_liveboard_sup.init/1","doc":"","ref":"nova_liveboard_sup.html#init/1"},{"type":"function","title":"nova_liveboard_sup.start_link/0","doc":"","ref":"nova_liveboard_sup.html#start_link/0"},{"type":"module","title":"nova_liveboard_trace_plugin","doc":"Nova plugin that feeds the liveboard's request tracer.\n\nRegister it as a global plugin in your Nova config to capture every request\nhandled by the node:\n\n {nova, [\n {plugins, [\n {pre_request, nova_liveboard_trace_plugin, #{}},\n {post_request, nova_liveboard_trace_plugin, #{}}\n ]}\n ]}.\n\n`pre_request/4` stamps the request with an id, start time, reductions baseline\nand the resolved handler MFA (from `Env#{callback}`), and - when deep trace is\narmed - turns on scoped process tracing of the handling process.\n`post_request/4` computes duration / reductions / peak memory / status and\nhands the summary to `nova_liveboard_tracer`. The whole chain runs in one\nprocess, so the marker stashed in `Req` survives from pre to post.\n\nThe dashboard's own routes are skipped so it never traces itself (its SSE\nstreams never return, and would otherwise never finalise).","ref":"nova_liveboard_trace_plugin.html"},{"type":"function","title":"nova_liveboard_trace_plugin.init/0","doc":"","ref":"nova_liveboard_trace_plugin.html#init/0"},{"type":"function","title":"nova_liveboard_trace_plugin.plugin_info/0","doc":"","ref":"nova_liveboard_trace_plugin.html#plugin_info/0"},{"type":"function","title":"nova_liveboard_trace_plugin.post_request/4","doc":"","ref":"nova_liveboard_trace_plugin.html#post_request/4"},{"type":"function","title":"nova_liveboard_trace_plugin.pre_request/4","doc":"","ref":"nova_liveboard_trace_plugin.html#pre_request/4"},{"type":"module","title":"nova_liveboard_tracer","doc":"Request store and deep-trace collector for the liveboard.\n\nTwo jobs in one `gen_server`:\n\n1. **Store** - a capped, newest-first ring of recently completed requests\n (`nova_liveboard:request_buffer/0` deep). `nova_liveboard_trace_plugin`\n calls `finalize/2` from each request's `post_request` hook; SSE feeds\n `subscribe/0` to it for live updates.\n\n2. **Trace collector** - when deep trace is armed (`start_trace/1`), the next\n N requests have their handling process traced with\n `erlang:trace(self(), true, [procs, set_on_spawn, {tracer, Pid}])` (the\n plugin enables it; this server is the `Pid`). It receives\n `{trace, Parent, spawn, Child, MFA}` messages, attributes each spawned\n process to the owning request by walking the parent chain, and attaches the\n resulting spawned-process tree to the request record on `finalize/2`.\n\nThe dashboard works without any of this; only the Requests page needs the\nplugin registered.","ref":"nova_liveboard_tracer.html"},{"type":"function","title":"nova_liveboard_tracer.active/0","doc":"Whether any request has been captured (a proxy for 'plugin is wired up').","ref":"nova_liveboard_tracer.html#active/0"},{"type":"function","title":"nova_liveboard_tracer.clear/0","doc":"","ref":"nova_liveboard_tracer.html#clear/0"},{"type":"function","title":"nova_liveboard_tracer.finalize/2","doc":"Record a completed request; attaches the spawned tree if it was traced.","ref":"nova_liveboard_tracer.html#finalize/2"},{"type":"function","title":"nova_liveboard_tracer.get/1","doc":"","ref":"nova_liveboard_tracer.html#get/1"},{"type":"function","title":"nova_liveboard_tracer.handle_call/3","doc":"","ref":"nova_liveboard_tracer.html#handle_call/3"},{"type":"function","title":"nova_liveboard_tracer.handle_cast/2","doc":"","ref":"nova_liveboard_tracer.html#handle_cast/2"},{"type":"function","title":"nova_liveboard_tracer.handle_info/2","doc":"","ref":"nova_liveboard_tracer.html#handle_info/2"},{"type":"function","title":"nova_liveboard_tracer.init/1","doc":"","ref":"nova_liveboard_tracer.html#init/1"},{"type":"function","title":"nova_liveboard_tracer.maybe_arm/2","doc":"Ask whether the request `ReqId` handled by `RootPid` should be deep-traced.\n\nReturns `trace` (and registers the root pid) when deep trace is armed, else\n`no_trace`. The caller (the request process) is responsible for calling\n`erlang:trace/3` on itself when `trace` is returned.","ref":"nova_liveboard_tracer.html#maybe_arm/2"},{"type":"function","title":"nova_liveboard_tracer.recent/0","doc":"","ref":"nova_liveboard_tracer.html#recent/0"},{"type":"function","title":"nova_liveboard_tracer.start_link/0","doc":"","ref":"nova_liveboard_tracer.html#start_link/0"},{"type":"function","title":"nova_liveboard_tracer.start_trace/1","doc":"","ref":"nova_liveboard_tracer.html#start_trace/1"},{"type":"function","title":"nova_liveboard_tracer.stop_trace/0","doc":"","ref":"nova_liveboard_tracer.html#stop_trace/0"},{"type":"function","title":"nova_liveboard_tracer.subscribe/0","doc":"Register the caller for live request + trace-state notifications.","ref":"nova_liveboard_tracer.html#subscribe/0"},{"type":"function","title":"nova_liveboard_tracer.trace_state/0","doc":"","ref":"nova_liveboard_tracer.html#trace_state/0"}],"proglang":"erlang","content_type":"text/markdown","producer":{"name":"ex_doc","version":"0.40.3"}} \ No newline at end of file diff --git a/doc/dist/sidebar_items-72351220.js b/doc/dist/sidebar_items-72351220.js new file mode 100644 index 0000000..c8e7f92 --- /dev/null +++ b/doc/dist/sidebar_items-72351220.js @@ -0,0 +1 @@ +sidebarNodes={"modules":[{"id":"nova_liveboard","deprecated":false,"group":"","title":"nova_liveboard","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"prefix/0","deprecated":false,"title":"prefix()","anchor":"prefix/0"},{"id":"refresh_ms/0","deprecated":false,"title":"refresh_ms()","anchor":"refresh_ms/0"},{"id":"request_buffer/0","deprecated":false,"title":"request_buffer()","anchor":"request_buffer/0"}],"key":"functions"}]},{"id":"nova_liveboard_action_controller","deprecated":false,"group":"","title":"nova_liveboard_action_controller","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"clear/1","deprecated":false,"title":"clear(Req)","anchor":"clear/1"},{"id":"trace_start/1","deprecated":false,"title":"trace_start(Req)","anchor":"trace_start/1"},{"id":"trace_stop/1","deprecated":false,"title":"trace_stop(Req)","anchor":"trace_stop/1"}],"key":"functions"}]},{"id":"nova_liveboard_app","deprecated":false,"group":"","title":"nova_liveboard_app","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"start/2","deprecated":false,"title":"start(StartType, StartArgs)","anchor":"start/2"},{"id":"stop/1","deprecated":false,"title":"stop(State)","anchor":"stop/1"}],"key":"functions"}]},{"id":"nova_liveboard_data","deprecated":false,"group":"","title":"nova_liveboard_data","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"collect_metrics/1","deprecated":false,"title":"collect_metrics(State)","anchor":"collect_metrics/1"},{"id":"ets_tables/0","deprecated":false,"title":"ets_tables()","anchor":"ets_tables/0"},{"id":"format_bytes/1","deprecated":false,"title":"format_bytes(Bytes)","anchor":"format_bytes/1"},{"id":"format_number/1","deprecated":false,"title":"format_number(N)","anchor":"format_number/1"},{"id":"format_uptime/1","deprecated":false,"title":"format_uptime(Ms)","anchor":"format_uptime/1"},{"id":"kura_available/0","deprecated":false,"title":"kura_available()","anchor":"kura_available/0"},{"id":"kura_migration_status/1","deprecated":false,"title":"kura_migration_status(RepoMod)","anchor":"kura_migration_status/1"},{"id":"kura_pool_stats/1","deprecated":false,"title":"kura_pool_stats(PoolName)","anchor":"kura_pool_stats/1"},{"id":"kura_repo_info/1","deprecated":false,"title":"kura_repo_info(RepoMod)","anchor":"kura_repo_info/1"},{"id":"kura_repos/0","deprecated":false,"title":"kura_repos()","anchor":"kura_repos/0"},{"id":"kura_schemas/1","deprecated":false,"title":"kura_schemas(RepoMod)","anchor":"kura_schemas/1"},{"id":"port_info/0","deprecated":false,"title":"port_info()","anchor":"port_info/0"},{"id":"running_applications/0","deprecated":false,"title":"running_applications()","anchor":"running_applications/0"},{"id":"scheduler_info/0","deprecated":false,"title":"scheduler_info()","anchor":"scheduler_info/0"},{"id":"sparkline_points/3","deprecated":false,"title":"sparkline_points(Values, Width, Height)","anchor":"sparkline_points/3"},{"id":"supervision_tree/1","deprecated":false,"title":"supervision_tree(AppName)","anchor":"supervision_tree/1"},{"id":"system_info/0","deprecated":false,"title":"system_info()","anchor":"system_info/0"},{"id":"top_processes/2","deprecated":false,"title":"top_processes(SortBy, Limit)","anchor":"top_processes/2"}],"key":"functions"}]},{"id":"nova_liveboard_html","deprecated":false,"group":"","title":"nova_liveboard_html","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"apps_html/1","deprecated":false,"title":"apps_html(Apps)","anchor":"apps_html/1"},{"id":"csp_headers/0","deprecated":false,"title":"csp_headers()","anchor":"csp_headers/0"},{"id":"database_html/1","deprecated":false,"title":"database_html/1","anchor":"database_html/1"},{"id":"ets_html/1","deprecated":false,"title":"ets_html(Tables)","anchor":"ets_html/1"},{"id":"html_escape/1","deprecated":false,"title":"html_escape/1","anchor":"html_escape/1"},{"id":"metrics_html/1","deprecated":false,"title":"metrics_html(M)","anchor":"metrics_html/1"},{"id":"overview_html/1","deprecated":false,"title":"overview_html(Sys)","anchor":"overview_html/1"},{"id":"page/4","deprecated":false,"title":"page(Active, StreamPath, VitalsInner, MainInner)","anchor":"page/4"},{"id":"ports_html/1","deprecated":false,"title":"ports_html(Ports)","anchor":"ports_html/1"},{"id":"processes_html/2","deprecated":false,"title":"processes_html(Procs, SortBy)","anchor":"processes_html/2"},{"id":"request_detail_html/1","deprecated":false,"title":"request_detail_html/1","anchor":"request_detail_html/1"},{"id":"request_row/1","deprecated":false,"title":"request_row(R)","anchor":"request_row/1"},{"id":"requests_html/3","deprecated":false,"title":"requests_html/3","anchor":"requests_html/3"},{"id":"requests_toolbar_html/1","deprecated":false,"title":"requests_toolbar_html(Trace)","anchor":"requests_toolbar_html/1"},{"id":"schemas_html/1","deprecated":false,"title":"schemas_html/1","anchor":"schemas_html/1"},{"id":"sup_tree_html/3","deprecated":false,"title":"sup_tree_html(Apps, Active, Result)","anchor":"sup_tree_html/3"},{"id":"vitals_html/1","deprecated":false,"title":"vitals_html(Sys)","anchor":"vitals_html/1"}],"key":"functions"}]},{"id":"nova_liveboard_page_controller","deprecated":false,"group":"","title":"nova_liveboard_page_controller","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"app_names/0","deprecated":false,"title":"app_names()","anchor":"app_names/0"},{"id":"applications/1","deprecated":false,"title":"applications(Req)","anchor":"applications/1"},{"id":"database/1","deprecated":false,"title":"database(Req)","anchor":"database/1"},{"id":"default_app/1","deprecated":false,"title":"default_app(Names)","anchor":"default_app/1"},{"id":"ets/1","deprecated":false,"title":"ets(Req)","anchor":"ets/1"},{"id":"index/1","deprecated":false,"title":"index(Req)","anchor":"index/1"},{"id":"metrics/1","deprecated":false,"title":"metrics(Req)","anchor":"metrics/1"},{"id":"page_atom/1","deprecated":false,"title":"page_atom/1","anchor":"page_atom/1"},{"id":"page_inner/2","deprecated":false,"title":"page_inner/2","anchor":"page_inner/2"},{"id":"ports/1","deprecated":false,"title":"ports(Req)","anchor":"ports/1"},{"id":"processes/1","deprecated":false,"title":"processes(Req)","anchor":"processes/1"},{"id":"request_show/1","deprecated":false,"title":"request_show(Req)","anchor":"request_show/1"},{"id":"requests/1","deprecated":false,"title":"requests(Req)","anchor":"requests/1"},{"id":"schemas/1","deprecated":false,"title":"schemas(Req)","anchor":"schemas/1"},{"id":"supervisors/1","deprecated":false,"title":"supervisors(Req)","anchor":"supervisors/1"}],"key":"functions"}]},{"id":"nova_liveboard_router","deprecated":false,"group":"","title":"nova_liveboard_router","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"routes/1","deprecated":false,"title":"routes(Env)","anchor":"routes/1"}],"key":"functions"}]},{"id":"nova_liveboard_sse","deprecated":false,"group":"","title":"nova_liveboard_sse","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"handle_stream/3","deprecated":false,"title":"handle_stream/3","anchor":"handle_stream/3"},{"id":"register/0","deprecated":false,"title":"register()","anchor":"register/0"},{"id":"stream/1","deprecated":false,"title":"stream(Req)","anchor":"stream/1"}],"key":"functions"}]},{"id":"nova_liveboard_sup","deprecated":false,"group":"","title":"nova_liveboard_sup","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"init/1","deprecated":false,"title":"init/1","anchor":"init/1"},{"id":"start_link/0","deprecated":false,"title":"start_link()","anchor":"start_link/0"}],"key":"functions"}]},{"id":"nova_liveboard_trace_plugin","deprecated":false,"group":"","title":"nova_liveboard_trace_plugin","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"init/0","deprecated":false,"title":"init()","anchor":"init/0"},{"id":"plugin_info/0","deprecated":false,"title":"plugin_info()","anchor":"plugin_info/0"},{"id":"post_request/4","deprecated":false,"title":"post_request(Req, Env, Opts, State)","anchor":"post_request/4"},{"id":"pre_request/4","deprecated":false,"title":"pre_request(Req, Env, Opts, State)","anchor":"pre_request/4"}],"key":"functions"}]},{"id":"nova_liveboard_tracer","deprecated":false,"group":"","title":"nova_liveboard_tracer","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"active/0","deprecated":false,"title":"active()","anchor":"active/0"},{"id":"clear/0","deprecated":false,"title":"clear()","anchor":"clear/0"},{"id":"finalize/2","deprecated":false,"title":"finalize(ReqId, Summary)","anchor":"finalize/2"},{"id":"get/1","deprecated":false,"title":"get(ReqId)","anchor":"get/1"},{"id":"handle_call/3","deprecated":false,"title":"handle_call/3","anchor":"handle_call/3"},{"id":"handle_cast/2","deprecated":false,"title":"handle_cast/2","anchor":"handle_cast/2"},{"id":"handle_info/2","deprecated":false,"title":"handle_info/2","anchor":"handle_info/2"},{"id":"init/1","deprecated":false,"title":"init/1","anchor":"init/1"},{"id":"maybe_arm/2","deprecated":false,"title":"maybe_arm(ReqId, RootPid)","anchor":"maybe_arm/2"},{"id":"recent/0","deprecated":false,"title":"recent()","anchor":"recent/0"},{"id":"start_link/0","deprecated":false,"title":"start_link()","anchor":"start_link/0"},{"id":"start_trace/1","deprecated":false,"title":"start_trace(N)","anchor":"start_trace/1"},{"id":"stop_trace/0","deprecated":false,"title":"stop_trace()","anchor":"stop_trace/0"},{"id":"subscribe/0","deprecated":false,"title":"subscribe()","anchor":"subscribe/0"},{"id":"trace_state/0","deprecated":false,"title":"trace_state()","anchor":"trace_state/0"}],"key":"functions"}]}],"extras":[{"id":"api-reference","group":"","title":"API Reference","headers":[{"id":"Modules","anchor":"modules"}]}],"tasks":[]} \ No newline at end of file diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..929106f --- /dev/null +++ b/doc/index.html @@ -0,0 +1,10 @@ + + + + + nova_liveboard v0.0.0+build.11.ref1019ff1 — Documentation + + + + + diff --git a/doc/nova_liveboard.epub b/doc/nova_liveboard.epub new file mode 100644 index 0000000000000000000000000000000000000000..ea18d61b36a3fca35ec54605d1e03a60dd41d48e GIT binary patch literal 28896 zcma&NW2`XUvMs!9`&oLHZQHhO+qP}nwr$(CZF_z1K40$1-8VVtWOllLbS0gtnl#Aev~2Y&sZjoT{?~#4zy`23w>EZiw=-5&f&>7Tm_)bu*K&4)1^@*4 z*C+pR)3>v;GB?zBGPkv%HMVm$p!6`ell#Z&|8L`8R=;8gwafqk0E~bD01*GNDl04? zr%3B$?qp?5<7Vb$ZS~)o_g2u99iT(;IaOWi$*9KCLusI<_k)B7Gwd27U9>i>+Gv*a z#JIb)1}E!U<7payr#bbqc3{?>*5S!PD;g0o2^{)sv?^w-s6p7GIMT9d-G&pqQKin6 zF10peSOzXlta_OgP;m6hg7&p{ zVVbd^pl0Or0uTjHqQ=wHvt27Pt3S&jzA+Gn7S24(Zv55b)$u3h?*J{j%m7@T)t;e2 zX;y+(s;7dO%{C(rCw!2@;2iEbLh+f5pU+$0U>jTDQys?)jiD2Xy^G3Uli%ZuiNHd9 z$CyzTZ`T{z`oY`}z<+lcAT|)4?muzi#{mF<|DP_iv31ecwK8`xHn7!qFw%8$&^I)8 z_@7<4rDkor$%626quXEp_aZL^l@zCisKu|q(jVXwI>Ba`4hD=mn~H^@JYEUg_RM!T z9>uu4S+jKfq5v|1?BxDt-f@(3C))SzczKc4x64Pwp_}YrXYC80H8P-5JkC-(M`Yfi z9h)XLRA8oXetcZq$BzO{^jqMvR~hECwNG`-=dn@alg5&JDI$ z?x@jE0uL!dJnyLjAQ||~@5syvsYvR4%nmVtNhtV~*5~tkyz($IfBRUu zLa0FjnK!6gu&CIl7AN% zd(4!FmxXI2fk-a~wP6s^8&?k#Ssc^r-w}urG-^pP@Ss5?`;b40VJmy21cKlsV>13) z5yI+zAt<~Jv_&4@^wr<%#WrT}LxCDL9F+|)MowWf8z9}CuTO*8&!16*n3@N0A(?;H zj=Ig&l^jPbQM-fCf(c;H!2cXJ>HYFKHGytu z>7yPW5++p)ngH$S+6^c#Uy9i=Yl1b?ECGGi(-X`x^f>FbItV*L(LV%Ad>dOF2+|=J z29jXMBUfk0er4HkfIM27ct%B0-&Ko8g&E5g3CBc`%2M<{Ei97c!Taq9mQKL|gC(X^R7OZG!m=_A7#2R;?0@=uvas&+l3`U4{cO#7B%v8+2C3 zJx$LHKNfi^yW^<^ZR9@#s2gZ8MbQZ5eOWMj# z1SZX`Z#HX0s1&);*P|gDPyuBfWO=>6fe5e@v>JU&R#&4((sh|{A#7=sfL6lYl&Fy0 zxzLRAAhAw{kSwxBnV1=6IWxy$LK=86Fsc~tr60F{Pe5HNk!A=92uaiUp@q9V?j`A= zx@^FIY>~ZH&(a_e40-|8g`_AQ(40c~ zPbW1Yj*x~tq3lmSn&gk?((nx&S&9Egn&sD;zwW^8Q@^Ar^?nn3&T)Zb16)%EH64(I ztV$J(+=SR-VQ6~dq$jjFGaVASQ6m{1{-RDp^OkFuQ&t>M=|&_Gb$e#vH?q-L#MSSj z_|vU#hMsfz-isApw{3}^w?{S0nIU0Tkmk2;Dyf6`*?FJx(-jWWatAIcKhMxo7Ouq@ zrQ~D)8Rr(LtxvU`=;np+ zw)z^jz69BrGv|%t2%)PnSfFc z9IQ$uIs^5J$S8A2*8!NwsEwXH^LI=pMavQ!x#dO-Qs-37XHiX?I5$>!Ny2t(pv^@9tS*N}}RNe8)|q?pp20}f3p5)Tv4h)JIOdX!)Q8`CHKk{vS%ED}95YYY^&ugA$^cwa+>zXV+3GqZ6{6O+lUyQ__pIEct#Rqus8yWv!#z zMrue;^~-p1OMf=fNkEDS{*V0z56;vpC-6r>;rfT;uH zId(ccIf9c_8U!zuLSF%asU|o;eLQ($hos*`=EOyK5vmP9aRRLmd5#$;( zfne4MINAXfz8izftvvNy#31=$uiu%b;!kLlY?G~;7hmmQnnwh21BbA1-HN_qWImc=!8#fN649uG+&4Za zx?Lha9UzK72!nj=7~JOF((;FU5#A(;K9~hTQJ;UxtI7M@!&@!-mxVmpRr}CJz}ye^ z&%y*eV0pL(`+B~uvF2Dr@AHu%DjW%M zyK-JNqI~`zTV06_ZRB3E5ID`!)4oBtWVZma(v{AzE=OEpYIDYHNV zX9YN%0R+0zY}NrgNC(p7)Y2|iB%$CEQ1I(5dNJ%k(HY)aBX|hlvERLWf0NJsbMt=r zclZ0}1PF~qvT$cjdyho2IK3{o`Q06(My0az7&xu!GCrQ~C*$+uk^QHlyo~hVm3SxZ)J#A$^1CYup3pSN4Jak8I_*l3IjI&* zB8O?XmhcQd(ZX=XA!&@_8A+NGwMr;%gNjo+GN#Q>8zhkd%v=8CUO~Ec0qprKDxH3 z{>;3L@1FmI)-=JbH+f&rSv9TKO3`_?0`^}haF=ccSvk%QMj^N#EtYsKoMYp@b}{G$ zkSzM5;gEBf3fw?O67C$NRtoKjuYlUHI=x$~%+(@bKAUn-eDJW+90FzRVokJ%EtNWo z%2j1}VQY1`0t3@k{y1$o;-|*)3~@=9a`pVE{dsDGm(c;J1eFj&cyy2oq~oq(bdqGU z6X*2@Laxl(Ino$5iy|eW0e3swf52eL+|HGq~9z@&T%ENCWKy; zS`&e!@GhCIsDoVIUg8vo8U&9lx?Fxo-BUZ1Xzd}M0sD3(zt2Xg&1O+|v_wi&6QX1x zOhWb-NoMIphOYjs9RihfyvU4RmP4Z^X zY_n9PHf|s!R2e#fqE4b%KZ7>S3p)wp(q{dI<)YEMvyg>_a|2lNNQU#vgz{JheYaQdMRFd8?X3@c+x&Oe zCgFy$`R8)UI>!q)t$o`3^_t`cSMUS+$eLT#G8_ll2fL#-9!K{*v2XX^NsYH$M0!SG z0Dv$^007+osx&w{+x;&pf{M27A{~nNZY|x;Bm$Am3cgyhh=5fhi9bs}z&%rY_-0yh zOTF!eOxSPC^^o`k$H*#j^^(T@Hyh9C4pwQb42_PqcL$d+F;%4%t+TILV^vW40TQ1y zgnI4jy$7~rH{SUDJ^eZx)>RYu5~g2kX-gk_p2Egiu%iCvi*$x(!ks@Jed!o|VH5|psnC%#6s!KXJv zrMvQwIFcoctjPGiS58q;ID;7JS%UO^0AvZdShPG1D4qUeu8oY*O#n{xfqiV@!2wu* zb%RbH3!j4_-9lA{0dOlSBe9{Mym_LGeLpOMsL)W8AvuTdJA^<p6>6RIvIdZSgrV$PI<=;qc*0Ra&()whaLy(& z=$;Fz%xE(m=vxE1v6ZBwHq?+7jvLnyHcsftq`4|YL+rMlc!5dhP?y!7eQ5st6)U$A z8d`L0u>g5~e-Qx@JY!HTX?m9^C`I`?XELH0Ov%`C!nG3Uv5-sOm<51kyKBtAC?8Nu zTi?4k{GmZpcy#I7Mct%nK7CcT?pePy8hMDV{RI(oPU7B`2&lPldv!u>%wRvjibCwFLyiszeqnjHnq zp$rB2oB~js&OqJT=Ub``-)rW>xGDoUI^cn&>A~8OjCz02!2CT3ws!hb#}Y;#^T+A~AVYjLMH zS9N))cm$Feg3RrBeFnEJUi1sx_9X^>*mdA>eI0Kpr*Ec8Jtu4aqWYKN2vQQ?6kmU3 zc&@tsdn(d>Gp_T30RS{0{dcM8_`l$XxBrt|v{&Sz>L(zNVQjj`1{Bn-1G>~;S`YDt zGCQ}jj~7cRy28-@{ftQ>n+#uUwZ@n`3N(&?ec5t!Ei&EZ@_s(Lh+Un{YjRGGHn{<| zxs*tYENfDcUUcf#A-xnQcxJ%K&CM-swUIqyk)|X`K}U>$_$WnuheJaZo8Vj!TuoYC@McM87|TSg@IQ(A57YCAC$(f*r@5?5$x0 zU^;2_ofGDMT^~bKg&A;3G0_hb@81pRbBe1|{Mq|0y24V(n z*4M=u)<5L!?SWki#U>7_r5z0k=G3%>Mrc<^QKQ4*7$Vj1=%mTV%zid z2znRyIl*+EBA-0qgRWSof z?`QRxkwwIqsy-kVzD-+b~qATXq6}fuz>o7qdVbg7_hlr7b8QX0F8PT54?6A~2y|zK= z-0^xiJHhmEaRxh%+}nBMRQ5CqD_FAQ042>G)>5t2_&T;Z`PWIjh8QS~mEWuh1EiW@ z@GhDoNAoZhJr5W_1L1Jq5Z7QY1#JVyKtSZHhO2Qt*m78bz=Uyf`+cx|qg?3@M`7FzA6<)9P9^T~3Dl3~|l1(K_=A^y_ z6pfeG8u);ZEsD>O;X^5H)QuIy?Q7X;7H1fl$Q2yY6i{c=ANz}H_M|N^k6SRyr8x>j zD&1{jN|dnRj|fCp=@WPUFHZdNu%;#Hi)4IxDu1A9y89H-JnKKMR!mrCFdX zvdXw-dL^cwaa4x@QBr{1F-Cnr70(u%#t{_YHGc>sG8)8{#c;q~#$Y-wSRear-3~1} zP~geHMWX=9I2!=`K^5L3W*?zuUe4Ia6@CP^hr>1w%gin;Csg7@6F@Iw`O|@yOymd% zyu)#D2Yz8RAJ((qkL|cJS2lrFt=;_IGczo)llSMEMGM%{MluaxQSvae=yDaykh-#@vS0?gW}_YNQD(20lu1afx{`^SZ7m5Nw$hme;T_Zz%d? zv92&NM%(a3@VP;mpYc5j%&K4Yd&UKy#I6fMzNc{|r-WoXz2Gn7_XwFME-p9g_fT$- z;Q%hOMvbTKYc*C6N|fKIz7%{6tKBjZ^dC)f%E@J6lx9+p3OJ|XcGH#ms!>*A@POLE zQ{U}dzJ;eFMJ>2a_}HJnwx5ghq;DHH++KmUycbt@+oSJ2#cRBJUw&CJkDC8*quu#= zZLWr+Hb>nZu00Q;r~WN2j$61P1)a{0vAvxNH<)$kP8aA@HEUbj5I!%ipyJiJ9lCZt znbx>Y7B|ns&ZIgI1$_MIFxz>YpN})`Emp3DX8=*!vytj#&w2qezkqi+qJOD7uetLM z`{RgS@&0asY)p#FrtBYrBQck?%}&kNsq}65|ytt8%_sd|I|=x^x;kM;zcrgC;A zB3=CvcxL_gvIMw+HW&1dN(uB&Y5Ff^F9%y^r~i%YRjf1_^AAYeex;0B_mev)!2!V` zjz?f5j0VX-l¬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 literal 0 HcmV?d00001 diff --git a/doc/nova_liveboard.html b/doc/nova_liveboard.html new file mode 100644 index 0000000..4651a0d --- /dev/null +++ b/doc/nova_liveboard.html @@ -0,0 +1,284 @@ + + + + + + + + + + + nova_liveboard — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

Configuration helpers for the liveboard.

All settings live under the nova_liveboard application env:

  • prefix (default "/liveboard") - the path the dashboard mounts on.
  • refresh_ms (default 2000) - how often the polled SSE streams repaint.
  • request_buffer (default 200) - how many recent requests the tracer keeps.
+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+
+ prefix() + +
+ +

The mount path, always as a binary with a leading slash.

+ +
+ +
+
+ refresh_ms() + +
+ +

Repaint interval for the polled (non-request) streams, in milliseconds.

+ +
+ +
+ + +

How many completed requests the tracer ring buffer retains.

+ +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

prefix()

+ + +
+
+ +
+ +
+ +
-spec prefix() -> binary().
+ +
+ +

The mount path, always as a binary with a leading slash.

+
+
+
+ +
+ + + +
+

refresh_ms()

+ + +
+
+ +
+ +
+ +
-spec refresh_ms() -> pos_integer().
+ +
+ +

Repaint interval for the polled (non-request) streams, in milliseconds.

+
+
+
+ +
+ + + +
+

request_buffer()

+ + +
+
+ +
+ +
+ +
-spec request_buffer() -> pos_integer().
+ +
+ +

How many completed requests the tracer ring buffer retains.

+
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_action_controller.html b/doc/nova_liveboard_action_controller.html new file mode 100644 index 0000000..ca3d25f --- /dev/null +++ b/doc/nova_liveboard_action_controller.html @@ -0,0 +1,264 @@ + + + + + + + + + + + nova_liveboard_action_controller — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_action_controller + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

POST handlers for the Requests page controls (arm / disarm / clear deep trace).

Each mutates nova_liveboard_tracer and returns a one-shot Datastar response: +a normal {status, 200, ...} reply whose body is SSE-framed patch events. +Nova's built-in handle_status sends it, and Datastar applies the patches to +the clicking client immediately. Other open clients converge via the tracer's +own notifications to their Requests stream.

+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+
+ clear(Req) + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

clear(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

trace_start(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

trace_stop(Req)

+ + +
+
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_app.html b/doc/nova_liveboard_app.html new file mode 100644 index 0000000..f2f45b9 --- /dev/null +++ b/doc/nova_liveboard_app.html @@ -0,0 +1,238 @@ + + + + + + + + + + + nova_liveboard_app — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_app + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

Application entry point.

Starts the supervision tree (the request tracer) and registers the +{stream, ...} Datastar SSE return-handler with Nova. The request-tracing +plugin is not auto-registered: it is a global Nova plugin, so the host wires +it into its own {nova, [{plugins, ...}]} config (see the README). The +dashboard's pages work either way; only the Requests feed needs the plugin.

+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ + + +
+
+ stop(State) + +
+ +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

start(StartType, StartArgs)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

stop(State)

+ + +
+
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_data.html b/doc/nova_liveboard_data.html new file mode 100644 index 0000000..6add7c0 --- /dev/null +++ b/doc/nova_liveboard_data.html @@ -0,0 +1,753 @@ + + + + + + + + + + + nova_liveboard_data — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_data + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + +
+ + +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ + + +
+
+ ets_tables() + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + + + + + +
+
+ kura_repos() + +
+ +
+ +
+ + +
+ +
+
+ port_info() + +
+ +
+ + + +
+ + +
+ + + + + +
+ + +
+ + + +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

collect_metrics(State)

+ + +
+
+ +
+ +
+ +
-spec collect_metrics(undefined | map()) -> map().
+ +
+ + +
+
+
+ +
+ + + +
+

ets_tables()

+ + +
+
+ +
+ +
+ +
-spec ets_tables() -> [map()].
+ +
+ + +
+
+
+ +
+ + + +
+

format_bytes(Bytes)

+ + +
+
+ +
+ +
+ +
-spec format_bytes(non_neg_integer()) -> binary().
+ +
+ + +
+
+
+ +
+ + + +
+

format_number(N)

+ + +
+
+ +
+ +
+ +
-spec format_number(non_neg_integer()) -> binary().
+ +
+ + +
+
+
+ +
+ + + +
+

format_uptime(Ms)

+ + +
+
+ +
+ +
+ +
-spec format_uptime(non_neg_integer()) -> binary().
+ +
+ + +
+
+
+ +
+ + + +
+

kura_available()

+ + +
+
+ +
+ +
+ +
-spec kura_available() -> boolean().
+ +
+ + +
+
+
+ +
+ + + +
+

kura_migration_status(RepoMod)

+ + +
+
+ +
+ +
+ +
-spec kura_migration_status(module()) -> [map()].
+ +
+ + +
+
+
+ +
+ + + +
+

kura_pool_stats(PoolName)

+ + +
+
+ +
+ +
+ +
-spec kura_pool_stats(atom()) -> map().
+ +
+ + +
+
+
+ +
+ + + +
+

kura_repo_info(RepoMod)

+ + +
+
+ +
+ +
+ +
-spec kura_repo_info(module()) -> map().
+ +
+ + +
+
+
+ +
+ + + +
+

kura_repos()

+ + +
+
+ +
+ +
+ +
-spec kura_repos() -> [module()].
+ +
+ + +
+
+
+ +
+ + + +
+

kura_schemas(RepoMod)

+ + +
+
+ +
+ +
+ +
-spec kura_schemas(module()) -> [map()].
+ +
+ + +
+
+
+ +
+ + + +
+

port_info()

+ + +
+
+ +
+ +
+ +
-spec port_info() -> [map()].
+ +
+ + +
+
+
+ +
+ + + +
+

running_applications()

+ + +
+
+ +
+ +
+ +
-spec running_applications() -> [map()].
+ +
+ + +
+
+
+ +
+ + + +
+

scheduler_info()

+ + +
+
+ +
+ +
+ +
-spec scheduler_info() -> [map()].
+ +
+ + +
+
+
+ +
+ + + +
+

sparkline_points(Values, Width, Height)

+ + +
+
+ +
+ +
+ +
-spec sparkline_points(list(), number(), number()) -> binary().
+ +
+ + +
+
+
+ +
+ + + +
+

supervision_tree(AppName)

+ + +
+
+ +
+ +
+ +
-spec supervision_tree(atom()) -> {ok, list()} | {error, term()}.
+ +
+ + +
+
+
+ +
+ + + +
+

system_info()

+ + +
+
+ +
+ +
+ +
-spec system_info() -> map().
+ +
+ + +
+
+
+ +
+ + + +
+

top_processes(SortBy, Limit)

+ + +
+
+ +
+ +
+ +
-spec top_processes(SortBy, Limit) -> [map()]
+                       when SortBy :: memory | reductions | message_queue_len, Limit :: pos_integer().
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_html.html b/doc/nova_liveboard_html.html new file mode 100644 index 0000000..febfda6 --- /dev/null +++ b/doc/nova_liveboard_html.html @@ -0,0 +1,779 @@ + + + + + + + + + + + nova_liveboard_html — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_html + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

Pure HTML rendering for the liveboard - "BEAM mission control".

Every function here takes already-gathered data (from nova_liveboard_data +and nova_liveboard_tracer) and returns iodata(). No process state, no +side effects beyond reading the static nova_liveboard:prefix/0 config, so +the whole module is unit-testable in isolation.

page/4 renders the full document shell (vitals deck + instrument nav + +main stage). The per-region functions (vitals_html/1, overview_html/1, +processes_html/2, ...) render the inner of a live region; the page +controller uses them for the first paint and nova_liveboard_sse re-emits +them as Datastar patches as the VM changes.

+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+ + +

Running applications table.

+ +
+ +
+ + +

The dashboard's response headers. Strict CSP: everything is same-origin +(self-hosted datastar.js, fonts, css), so any stray off-origin request fails +loudly. unsafe-eval is required only for Datastar's data-* expression +evaluation; the privacy-critical directives stay 'self'.

+ +
+ +
+ + +

Kura repo pool panels.

+ +
+ +
+ + +

ETS tables table.

+ +
+ +
+ + +

Escape & < > " for safe interpolation into HTML.

+ +
+ +
+ + +

Live sparklines for memory/IO/run-queue + scheduler utilisation bars.

+ +
+ +
+ + +

System identity + memory breakdown + capacity panels.

+ +
+ +
+ + +

Full HTML document: head + vitals deck + nav + the main stage.

+ +
+ +
+ + +

Open ports table.

+ +
+ +
+ + +

Top processes table, sorted by SortBy, with sort controls.

+ +
+ +
+ + +

Detail page for one captured request: timings + spawned-process tree.

+ +
+ +
+ + +

A single request row (also emitted as a prepended SSE patch on arrival).

+ +
+ +
+ + +

The live request feed. Enabled reflects whether the tracing plugin is +registered; when it is not, a setup hint is shown instead of an empty feed. +Trace is the deep-trace arming state from nova_liveboard_tracer.

+ +
+ +
+ + +

The arm/disarm/clear toolbar (patched on its own as trace state changes).

+ +
+ +
+ + +

Kura schema definitions (fields, associations, indexes).

+ +
+ +
+ + +

The supervision tree for Active (static process structure) + app chooser.

+ +
+ +
+ + +

The live header gauges: capacity vs limits + memory + uptime.

+ +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

apps_html(Apps)

+ + +
+
+ +
+ +
+ +
-spec apps_html([map()]) -> iodata().
+ +
+ +

Running applications table.

+
+
+
+ +
+ + + +
+

csp_headers()

+ + +
+
+ +
+ +
+ +
-spec csp_headers() -> map().
+ +
+ +

The dashboard's response headers. Strict CSP: everything is same-origin +(self-hosted datastar.js, fonts, css), so any stray off-origin request fails +loudly. unsafe-eval is required only for Datastar's data-* expression +evaluation; the privacy-critical directives stay 'self'.

+
+
+
+ +
+ + + +
+

database_html/1

+ + +
+
+ +
+ +
+ +
-spec database_html([map()]) -> iodata().
+ +
+ +

Kura repo pool panels.

+
+
+
+ +
+ + + +
+

ets_html(Tables)

+ + +
+
+ +
+ +
+ +
-spec ets_html([map()]) -> iodata().
+ +
+ +

ETS tables table.

+
+
+
+ +
+ + + +
+

html_escape/1

+ + +
+
+ +
+ +
+ +
-spec html_escape(iodata() | atom() | integer()) -> binary().
+ +
+ +

Escape & < > " for safe interpolation into HTML.

+
+
+
+ +
+ + + +
+

metrics_html(M)

+ + +
+
+ +
+ +
+ +
-spec metrics_html(map()) -> iodata().
+ +
+ +

Live sparklines for memory/IO/run-queue + scheduler utilisation bars.

+
+
+
+ +
+ + + +
+

overview_html(Sys)

+ + +
+
+ +
+ +
+ +
-spec overview_html(map()) -> iodata().
+ +
+ +

System identity + memory breakdown + capacity panels.

+
+
+
+ +
+ + + +
+

page(Active, StreamPath, VitalsInner, MainInner)

+ + +
+
+ +
+ +
+ +
-spec page(atom(), binary() | none, iodata(), iodata()) -> iodata().
+ +
+ +

Full HTML document: head + vitals deck + nav + the main stage.

StreamPath is the SSE path the main region should subscribe to (so it +repaints live), or none for a static page (e.g. a request detail) that must +not be overwritten by a stream.

+
+
+
+ +
+ + + +
+

ports_html(Ports)

+ + +
+
+ +
+ +
+ +
-spec ports_html([map()]) -> iodata().
+ +
+ +

Open ports table.

+
+
+
+ +
+ + + +
+

processes_html(Procs, SortBy)

+ + +
+
+ +
+ +
+ +
-spec processes_html([map()], atom()) -> iodata().
+ +
+ +

Top processes table, sorted by SortBy, with sort controls.

+
+
+
+ +
+ + + +
+

request_detail_html/1

+ + +
+
+ +
+ +
+ +
-spec request_detail_html(map() | undefined) -> iodata().
+ +
+ +

Detail page for one captured request: timings + spawned-process tree.

+
+
+
+ +
+ + + +
+

request_row(R)

+ + +
+
+ +
+ +
+ +
-spec request_row(map()) -> iodata().
+ +
+ +

A single request row (also emitted as a prepended SSE patch on arrival).

+
+
+
+ +
+ + + +
+

requests_html/3

+ + +
+
+ +
+ +
+ +
-spec requests_html([map()], map(), boolean()) -> iodata().
+ +
+ +

The live request feed. Enabled reflects whether the tracing plugin is +registered; when it is not, a setup hint is shown instead of an empty feed. +Trace is the deep-trace arming state from nova_liveboard_tracer.

+
+
+
+ +
+ + + +
+

requests_toolbar_html(Trace)

+ + +
+
+ +
+ +
+ +
-spec requests_toolbar_html(map()) -> iodata().
+ +
+ +

The arm/disarm/clear toolbar (patched on its own as trace state changes).

+
+
+
+ +
+ + + +
+

schemas_html/1

+ + +
+
+ +
+ +
+ +
-spec schemas_html([map()]) -> iodata().
+ +
+ +

Kura schema definitions (fields, associations, indexes).

+
+
+
+ +
+ + + +
+

sup_tree_html(Apps, Active, Result)

+ + +
+
+ +
+ +
+ +
-spec sup_tree_html([atom()], atom(), {ok, list()} | {error, term()}) -> iodata().
+ +
+ +

The supervision tree for Active (static process structure) + app chooser.

+
+
+
+ +
+ + + +
+

vitals_html(Sys)

+ + +
+
+ +
+ +
+ +
-spec vitals_html(map()) -> iodata().
+ +
+ +

The live header gauges: capacity vs limits + memory + uptime.

+
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_page_controller.html b/doc/nova_liveboard_page_controller.html new file mode 100644 index 0000000..113d500 --- /dev/null +++ b/doc/nova_liveboard_page_controller.html @@ -0,0 +1,603 @@ + + + + + + + + + + + nova_liveboard_page_controller — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_page_controller + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

Nova controllers for the liveboard pages.

Each page server-renders the full mission-control shell (nova_liveboard_html: page/4) with a first-paint snapshot, then a data-init stream takes over and +repaints the live region. page_inner/2 is the single place that maps a page +to its data + HTML; both these controllers and nova_liveboard_sse call it, so +the first paint and the live patches always agree.

+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+
+ app_names() + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ ets(Req) + +
+ +
+ +
+
+ index(Req) + +
+ +
+ +
+
+ metrics(Req) + +
+ +
+ +
+
+ page_atom/1 + +
+ +

Binary page name (from a route binding) to its page atom.

+ +
+ +
+
+ page_inner/2 + +
+ +

Map a page to its freshly-gathered data, rendered to the live region inner.

+ +
+ +
+
+ ports(Req) + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ schemas(Req) + +
+ +
+ +
+ + +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

app_names()

+ + +
+
+ +
+ +
+ +
-spec app_names() -> [atom()].
+ +
+ + +
+
+
+ +
+ + + +
+

applications(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

database(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

default_app(Names)

+ + +
+
+ +
+ +
+ +
-spec default_app([atom()]) -> atom().
+ +
+ + +
+
+
+ +
+ + + +
+

ets(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

index(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

metrics(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

page_atom/1

+ + +
+
+ +
+ +
+ +
-spec page_atom(binary()) -> atom().
+ +
+ +

Binary page name (from a route binding) to its page atom.

+
+
+
+ +
+ + + +
+

page_inner/2

+ + +
+
+ +
+ +
+ +
-spec page_inner(atom(), term()) -> iodata().
+ +
+ +

Map a page to its freshly-gathered data, rendered to the live region inner.

+
+
+
+ +
+ + + +
+

ports(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

processes(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

request_show(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

requests(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

schemas(Req)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

supervisors(Req)

+ + +
+
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_router.html b/doc/nova_liveboard_router.html new file mode 100644 index 0000000..d94673e --- /dev/null +++ b/doc/nova_liveboard_router.html @@ -0,0 +1,202 @@ + + + + + + + + + + + nova_liveboard_router — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_router + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + +
+ + +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+
+ routes(Env) + +
+ +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

routes(Env)

+ + +
+
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_sse.html b/doc/nova_liveboard_sse.html new file mode 100644 index 0000000..55f999f --- /dev/null +++ b/doc/nova_liveboard_sse.html @@ -0,0 +1,276 @@ + + + + + + + + + + + nova_liveboard_sse — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_sse + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

The liveboard's live transport: one long-lived SSE stream per page region.

stream/1 is a normal Nova controller returning {stream, Code, Headers, Source}. That tuple is picked up by a return-handler we register +(handle_stream/3), which stream_replys the SSE headers and then holds the +connection, pushing datastar:patch_elements/2 frames and never returning - +so Nova's buffered render_response (which would reply again and crash) never +runs. On client disconnect the next stream_body fails and the request process +exits, dropping any tracer subscription. See novaframework/nova#387.

Two stream shapes:

  • polled (vitals/overview/processes/ets/applications/ports/supervisors/ +metrics): a receive ... after RefreshMs timer repaints the region. The +metrics stream threads the nova_liveboard_data:collect_metrics/1 +accumulator through its loop so sparklines and scheduler deltas build up.
  • event-driven (requests): subscribes to nova_liveboard_tracer and +prepends each request as it completes; no polling.

The handler is registered as an explicit fun/3 because current Nova invokes +return-handlers with 3 args, while its {Mod, Fun} form wraps to arity 4.

+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+ + +
+ +
+
+ register() + +
+ +
+ +
+
+ stream(Req) + +
+ +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

handle_stream/3

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

register()

+ + +
+
+ +
+ +
+ +
-spec register() -> ok | {error, atom()}.
+ +
+ + +
+
+
+ +
+ + + +
+

stream(Req)

+ + +
+
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_sup.html b/doc/nova_liveboard_sup.html new file mode 100644 index 0000000..8cccb3c --- /dev/null +++ b/doc/nova_liveboard_sup.html @@ -0,0 +1,240 @@ + + + + + + + + + + + nova_liveboard_sup — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_sup + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

Top supervisor: owns the request tracer.

+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+
+ init/1 + +
+ +
+ +
+
+ start_link() + +
+ +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

init/1

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

start_link()

+ + +
+
+ +
+ +
+ +
-spec start_link() -> {ok, pid()}.
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_trace_plugin.html b/doc/nova_liveboard_trace_plugin.html new file mode 100644 index 0000000..2520529 --- /dev/null +++ b/doc/nova_liveboard_trace_plugin.html @@ -0,0 +1,304 @@ + + + + + + + + + + + nova_liveboard_trace_plugin — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_trace_plugin + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

Nova plugin that feeds the liveboard's request tracer.

Register it as a global plugin in your Nova config to capture every request +handled by the node:

{nova, [
+  {plugins, [
+    {pre_request,  nova_liveboard_trace_plugin, #{}},
+    {post_request, nova_liveboard_trace_plugin, #{}}
+  ]}
+]}.

pre_request/4 stamps the request with an id, start time, reductions baseline +and the resolved handler MFA (from Env#{callback}), and - when deep trace is +armed - turns on scoped process tracing of the handling process. +post_request/4 computes duration / reductions / peak memory / status and +hands the summary to nova_liveboard_tracer. The whole chain runs in one +process, so the marker stashed in Req survives from pre to post.

The dashboard's own routes are skipped so it never traces itself (its SSE +streams never return, and would otherwise never finalise).

+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+
+ init() + +
+ +
+ +
+ + +
+ + + + + +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

init()

+ + +
+
+ +
+ +
+ +
-spec init() -> {ok, undefined}.
+ +
+ + +
+
+
+ +
+ + + +
+

plugin_info()

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

post_request(Req, Env, Opts, State)

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

pre_request(Req, Env, Opts, State)

+ + +
+
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/nova_liveboard_tracer.html b/doc/nova_liveboard_tracer.html new file mode 100644 index 0000000..e2b4b6d --- /dev/null +++ b/doc/nova_liveboard_tracer.html @@ -0,0 +1,658 @@ + + + + + + + + + + + nova_liveboard_tracer — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + +
+
+

+ nova_liveboard_tracer + (nova_liveboard v0.0.0+build.11.ref1019ff1) + +

+ + + + +
+ + +
+

Request store and deep-trace collector for the liveboard.

Two jobs in one gen_server:

  1. Store - a capped, newest-first ring of recently completed requests +(nova_liveboard:request_buffer/0 deep). nova_liveboard_trace_plugin +calls finalize/2 from each request's post_request hook; SSE feeds +subscribe/0 to it for live updates.

  2. Trace collector - when deep trace is armed (start_trace/1), the next +N requests have their handling process traced with +erlang:trace(self(), true, [procs, set_on_spawn, {tracer, Pid}]) (the +plugin enables it; this server is the Pid). It receives +{trace, Parent, spawn, Child, MFA} messages, attributes each spawned +process to the owning request by walking the parent chain, and attaches the +resulting spawned-process tree to the request record on finalize/2.

The dashboard works without any of this; only the Requests page needs the +plugin registered.

+
+ +
+ +
+

+ + + + Summary +

+
+

+ Functions +

+ +
+
+ active() + +
+ +

Whether any request has been captured (a proxy for 'plugin is wired up').

+ +
+ +
+
+ clear() + +
+ +
+ +
+ + +

Record a completed request; attaches the spawned tree if it was traced.

+ +
+ +
+
+ get(ReqId) + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ init/1 + +
+ +
+ +
+ + +

Ask whether the request ReqId handled by RootPid should be deep-traced.

+ +
+ +
+
+ recent() + +
+ +
+ +
+
+ start_link() + +
+ +
+ +
+ + +
+ +
+
+ stop_trace() + +
+ +
+ +
+
+ subscribe() + +
+ +

Register the caller for live request + trace-state notifications.

+ +
+ +
+ + +
+ +
+ +
+ + +
+

+ + + + Functions +

+ +
+
+ +
+ + + +
+

active()

+ + +
+
+ +
+ +
+ +
-spec active() -> boolean().
+ +
+ +

Whether any request has been captured (a proxy for 'plugin is wired up').

+
+
+
+ +
+ + + +
+

clear()

+ + +
+
+ +
+ +
+ +
-spec clear() -> ok.
+ +
+ + +
+
+
+ +
+ + + +
+

finalize(ReqId, Summary)

+ + +
+
+ +
+ +
+ +
-spec finalize(binary(), map()) -> ok.
+ +
+ +

Record a completed request; attaches the spawned tree if it was traced.

+
+
+
+ +
+ + + +
+

get(ReqId)

+ + +
+
+ +
+ +
+ +
-spec get(binary()) -> map() | undefined.
+ +
+ + +
+
+
+ +
+ + + +
+

handle_call/3

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

handle_cast/2

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

handle_info/2

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

init/1

+ + +
+
+ +
+ + +
+
+
+ +
+ + + +
+

maybe_arm(ReqId, RootPid)

+ + +
+
+ +
+ +
+ +
-spec maybe_arm(binary(), pid()) -> trace | no_trace.
+ +
+ +

Ask whether the request ReqId handled by RootPid should be deep-traced.

Returns trace (and registers the root pid) when deep trace is armed, else +no_trace. The caller (the request process) is responsible for calling +erlang:trace/3 on itself when trace is returned.

+
+
+
+ +
+ + + +
+

recent()

+ + +
+
+ +
+ +
+ +
-spec recent() -> [map()].
+ +
+ + +
+
+
+ +
+ + + +
+

start_link()

+ + +
+
+ +
+ +
+ +
-spec start_link() -> {ok, pid()}.
+ +
+ + +
+
+
+ +
+ + + +
+

start_trace(N)

+ + +
+
+ +
+ +
+ +
-spec start_trace(pos_integer()) -> ok.
+ +
+ + +
+
+
+ +
+ + + +
+

stop_trace()

+ + +
+
+ +
+ +
+ +
-spec stop_trace() -> ok.
+ +
+ + +
+
+
+ +
+ + + +
+

subscribe()

+ + +
+
+ +
+ +
+ +
-spec subscribe() -> ok.
+ +
+ +

Register the caller for live request + trace-state notifications.

+
+
+
+ +
+ + + +
+

trace_state()

+ + +
+
+ +
+ +
+ +
-spec trace_state() -> map().
+ +
+ + +
+
+ +
+
+ + +
+
+
+ + + diff --git a/doc/search.html b/doc/search.html new file mode 100644 index 0000000..268540b --- /dev/null +++ b/doc/search.html @@ -0,0 +1,142 @@ + + + + + + + + + + + + + Search — nova_liveboard v0.0.0+build.11.ref1019ff1 + + + + + + + + + + + +
+ + + + + + + +
+
+ + + + + +
+
+
+ + + diff --git a/rebar.config b/rebar.config index fb88e18..1c909f7 100644 --- a/rebar.config +++ b/rebar.config @@ -12,7 +12,8 @@ ]}. {project_plugins, [ - {erlfmt, "~>1.7"} + {erlfmt, "~>1.7"}, + covertool ]}. {erlfmt, [ diff --git a/rebar.lock b/rebar.lock index b661b42..69132d7 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,33 +1,33 @@ {"1.2.0", -[{<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.13.0">>},1}, +[{<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.15.0">>},1}, {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.16.1">>},2}, {<<"datastar">>, {git,"https://github.com/Taure/datastar.git", {ref,"fcc11f399294b305620a7c99ed633b898ce3889b"}}, 0}, {<<"erlydtl">>,{pkg,<<"erlydtl">>,<<"0.14.0">>},1}, - {<<"jhn_stdlib">>,{pkg,<<"jhn_stdlib">>,<<"5.4.0">>},1}, + {<<"jhn_stdlib">>,{pkg,<<"jhn_stdlib">>,<<"5.11.2">>},1}, {<<"nova">>, {git,"https://github.com/novaframework/nova.git", - {ref,"7a474e312344d507db5f216569d4c31b98727d77"}}, + {ref,"9ee7e21ef5f82c9c8b71b0ff6bf0ace08d8b72a3"}}, 0}, {<<"ranch">>,{pkg,<<"ranch">>,<<"2.2.0">>},2}, {<<"routing_tree">>,{pkg,<<"routing_tree">>,<<"1.0.11">>},1}, {<<"thoas">>,{pkg,<<"thoas">>,<<"1.2.1">>},1}]}. [ {pkg_hash,[ - {<<"cowboy">>, <<"09D770DD5F6A22CC60C071F432CD7CB87776164527F205C5A6B0F24FF6B38990">>}, + {<<"cowboy">>, <<"9CFE86ED7117BF045E10ADBEDB0170AF7BE57F2A3637E7BE143433D8DD267396">>}, {<<"cowlib">>, <<"318D385D55F657E9A5005838C4E426E13DCD724A691438384B6165A69687E531">>}, {<<"erlydtl">>, <<"964B2DC84F8C17ACFAA69C59BA129EF26AC45D2BA898C3C6AD9B5BDC8BA13CED">>}, - {<<"jhn_stdlib">>, <<"FAC6F19B35351278F1CB156E23A5B2A6047A9DD5AB1FD9E1189A7918006DF7ED">>}, + {<<"jhn_stdlib">>, <<"785074F3CA368EAA8E9AF1592BC19AE9EF1F7AF30B2CD6456A6083173A8F5CCB">>}, {<<"ranch">>, <<"25528F82BC8D7C6152C57666CA99EC716510FE0925CB188172F41CE93117B1B0">>}, {<<"routing_tree">>, <<"72ACEF2095F0EC804F7AFD07EF781DDE5009425A1CA0A28F0706B1DB334A4812">>}, {<<"thoas">>, <<"19A25F31177A17E74004D4840F66D791D4298C5738790FA2CC73731EB911F195">>}]}, {pkg_hash_ext,[ - {<<"cowboy">>, <<"E724D3A70995025D654C1992C7B11DBFEA95205C047D86FF9BF1CDA92DDC5614">>}, + {<<"cowboy">>, <<"179FB65140FB440A17B767AD53B755081506F9596C4DB5C49C0396D8C8643668">>}, {<<"cowlib">>, <<"58F1E425A9E04176F1D30E20116F57C4E90EF0E187552E9741C465BDF4044F70">>}, {<<"erlydtl">>, <<"D80EC044CD8F58809C19D29AC5605BE09E955040911B644505E31E9DD8143431">>}, - {<<"jhn_stdlib">>, <<"7EABD1B01D2DEFF495BF7C5CA1DBA4D3FA0B84DC3AF03CA85F31D52EBB03C6FC">>}, + {<<"jhn_stdlib">>, <<"2329CD16DEE46704AAB6184D09508E59DBA31C4D3255271DBB7D34D115ECA508">>}, {<<"ranch">>, <<"FA0B99A1780C80218A4197A59EA8D3BDAE32FBFF7E88527D7D8A4787EFF4F8E7">>}, {<<"routing_tree">>, <<"85982C7AC502892C5179CD2A591331003BACD2D2A71723640BA7D23F45408E6E">>}, {<<"thoas">>, <<"E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A">>}]} diff --git a/rebar3.crashdump b/rebar3.crashdump new file mode 100644 index 0000000..fa6b490 --- /dev/null +++ b/rebar3.crashdump @@ -0,0 +1,25 @@ +Error: badarg +[{io,format, + ["\e[0;35m===> ~s ~s (~s)~n\e[0m", + [[128994,32,76,79,87,32,32,32,32,32],<<"cowlib">>,<<"2.16.1">>]], + [{file,"io.erl"}, + {line,205}, + {error_info,#{cause => {device,format},module => erl_stdlib_errors}}]}, + {rebar3_audit_prv,report_vuln,1, + [{file,"/home/dnwid/ai/personal/nova_liveboard/_build/default/plugins/rebar3_audit/src/rebar3_audit_prv.erl"}, + {line,204}]}, + {lists,foreach_1,2,[{file,"lists.erl"},{line,2641}]}, + {rebar3_audit_prv,do,1, + [{file,"/home/dnwid/ai/personal/nova_liveboard/_build/default/plugins/rebar3_audit/src/rebar3_audit_prv.erl"}, + {line,54}]}, + {rebar_core,do,2, + [{file,"/home/runner/work/rebar3/rebar3/apps/rebar/src/rebar_core.erl"}, + {line,155}]}, + {rebar3,run_aux,2, + [{file,"/home/runner/work/rebar3/rebar3/apps/rebar/src/rebar3.erl"}, + {line,212}]}, + {rebar3,main,1, + [{file,"/home/runner/work/rebar3/rebar3/apps/rebar/src/rebar3.erl"}, + {line,66}]}, + {escript,run,2,[{file,"escript.erl"},{line,908}]}] + From 58923eb1e0d092aecb853b7cdc2d87cd11709d1b Mon Sep 17 00:00:00 2001 From: Daniel Widgren Date: Thu, 28 May 2026 06:59:39 +0200 Subject: [PATCH 4/5] chore: stop tracking generated doc/ and crashdump artifacts --- .gitignore | 3 + doc/.build | 24 - doc/.build.epub | 1 - doc/404.html | 144 ---- doc/api-reference.html | 243 ------ doc/dist/html-Y2MUTVIN.js | 210 ----- doc/dist/html-erlang-KCHZLXSC.css | 6 - doc/dist/lato-latin-400-normal-W7754I4D.woff2 | Bin 23580 -> 0 bytes doc/dist/lato-latin-700-normal-2XVSBPG4.woff2 | Bin 23040 -> 0 bytes .../lato-latin-ext-400-normal-N27NCBWW.woff2 | Bin 5472 -> 0 bytes .../lato-latin-ext-700-normal-Q2L5DVMW.woff2 | Bin 5368 -> 0 bytes doc/dist/remixicon-ZWU5GQ4X.woff2 | Bin 2268 -> 0 bytes doc/dist/search_data-85BCB47E.js | 1 - doc/dist/sidebar_items-72351220.js | 1 - doc/index.html | 10 - doc/nova_liveboard.epub | Bin 28896 -> 0 bytes doc/nova_liveboard.html | 284 ------- doc/nova_liveboard_action_controller.html | 264 ------ doc/nova_liveboard_app.html | 238 ------ doc/nova_liveboard_data.html | 753 ----------------- doc/nova_liveboard_html.html | 779 ------------------ doc/nova_liveboard_page_controller.html | 603 -------------- doc/nova_liveboard_router.html | 202 ----- doc/nova_liveboard_sse.html | 276 ------- doc/nova_liveboard_sup.html | 240 ------ doc/nova_liveboard_trace_plugin.html | 304 ------- doc/nova_liveboard_tracer.html | 658 --------------- doc/search.html | 142 ---- rebar3.crashdump | 25 - 29 files changed, 3 insertions(+), 5408 deletions(-) delete mode 100644 doc/.build delete mode 100644 doc/.build.epub delete mode 100644 doc/404.html delete mode 100644 doc/api-reference.html delete mode 100644 doc/dist/html-Y2MUTVIN.js delete mode 100644 doc/dist/html-erlang-KCHZLXSC.css delete mode 100644 doc/dist/lato-latin-400-normal-W7754I4D.woff2 delete mode 100644 doc/dist/lato-latin-700-normal-2XVSBPG4.woff2 delete mode 100644 doc/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 delete mode 100644 doc/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 delete mode 100644 doc/dist/remixicon-ZWU5GQ4X.woff2 delete mode 100644 doc/dist/search_data-85BCB47E.js delete mode 100644 doc/dist/sidebar_items-72351220.js delete mode 100644 doc/index.html delete mode 100644 doc/nova_liveboard.epub delete mode 100644 doc/nova_liveboard.html delete mode 100644 doc/nova_liveboard_action_controller.html delete mode 100644 doc/nova_liveboard_app.html delete mode 100644 doc/nova_liveboard_data.html delete mode 100644 doc/nova_liveboard_html.html delete mode 100644 doc/nova_liveboard_page_controller.html delete mode 100644 doc/nova_liveboard_router.html delete mode 100644 doc/nova_liveboard_sse.html delete mode 100644 doc/nova_liveboard_sup.html delete mode 100644 doc/nova_liveboard_trace_plugin.html delete mode 100644 doc/nova_liveboard_tracer.html delete mode 100644 doc/search.html delete mode 100644 rebar3.crashdump diff --git a/.gitignore b/.gitignore index 69fa449..6a53e20 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ _build/ +doc/ +*.crashdump +erl_crash.dump diff --git a/doc/.build b/doc/.build deleted file mode 100644 index a5575a8..0000000 --- a/doc/.build +++ /dev/null @@ -1,24 +0,0 @@ -404.html -api-reference.html -dist/html-Y2MUTVIN.js -dist/html-erlang-KCHZLXSC.css -dist/lato-latin-400-normal-W7754I4D.woff2 -dist/lato-latin-700-normal-2XVSBPG4.woff2 -dist/lato-latin-ext-400-normal-N27NCBWW.woff2 -dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 -dist/remixicon-ZWU5GQ4X.woff2 -dist/search_data-85BCB47E.js -dist/sidebar_items-72351220.js -index.html -nova_liveboard.html -nova_liveboard_action_controller.html -nova_liveboard_app.html -nova_liveboard_data.html -nova_liveboard_html.html -nova_liveboard_page_controller.html -nova_liveboard_router.html -nova_liveboard_sse.html -nova_liveboard_sup.html -nova_liveboard_trace_plugin.html -nova_liveboard_tracer.html -search.html diff --git a/doc/.build.epub b/doc/.build.epub deleted file mode 100644 index 8886625..0000000 --- a/doc/.build.epub +++ /dev/null @@ -1 +0,0 @@ -doc/nova_liveboard.epub diff --git a/doc/404.html b/doc/404.html deleted file mode 100644 index a635e6e..0000000 --- a/doc/404.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - 404 — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -

- Page not found -

- -

Sorry, but the page you were trying to get to, does not exist. You -may want to try searching this site using the sidebar - - or using our API Reference page - -to find what you were looking for.

- -
-
-
- - - diff --git a/doc/api-reference.html b/doc/api-reference.html deleted file mode 100644 index c739fe1..0000000 --- a/doc/api-reference.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - - - - - - API Reference — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

API Reference nova_liveboard v#0.0.0+build.11.ref1019ff1

- - -
- -
-

Modules

-
-
- - -

Configuration helpers for the liveboard.

- -
-
- - -

POST handlers for the Requests page controls (arm / disarm / clear deep trace).

- -
-
- - -

Application entry point.

- -
-
- - -
-
- - -

Pure HTML rendering for the liveboard - "BEAM mission control".

- -
-
- - -

Nova controllers for the liveboard pages.

- -
- -
- - -

The liveboard's live transport: one long-lived SSE stream per page region.

- -
-
- - -

Top supervisor: owns the request tracer.

- -
-
- - -

Nova plugin that feeds the liveboard's request tracer.

- -
-
- - -

Request store and deep-trace collector for the liveboard.

- -
- -
-
- - -
- -
-
-
- - - diff --git a/doc/dist/html-Y2MUTVIN.js b/doc/dist/html-Y2MUTVIN.js deleted file mode 100644 index b3ec4cd..0000000 --- a/doc/dist/html-Y2MUTVIN.js +++ /dev/null @@ -1,210 +0,0 @@ -(()=>{var _s=Object.create;var jn=Object.defineProperty;var Ls=Object.getOwnPropertyDescriptor;var Ts=Object.getOwnPropertyNames;var Ps=Object.getPrototypeOf,Os=Object.prototype.hasOwnProperty;var _=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var As=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Ts(t))!Os.call(e,i)&&i!==n&&jn(e,i,{get:()=>t[i],enumerable:!(r=Ls(t,i))||r.enumerable});return e};var Y=(e,t,n)=>(n=e!=null?_s(Ps(e)):{},As(t||!e||!e.__esModule?jn(n,"default",{value:e,enumerable:!0}):n,e));var fr=_((mc,hr)=>{var dr="Expected a function",ur=NaN,Ds="[object Symbol]",Bs=/^\s+|\s+$/g,Qs=/^[-+]0x[0-9a-f]+$/i,qs=/^0b[01]+$/i,Fs=/^0o[0-7]+$/i,Vs=parseInt,$s=typeof global=="object"&&global&&global.Object===Object&&global,Us=typeof self=="object"&&self&&self.Object===Object&&self,js=$s||Us||Function("return this")(),Ws=Object.prototype,zs=Ws.toString,Gs=Math.max,Ks=Math.min,Ht=function(){return js.Date.now()};function Ys(e,t,n){var r,i,s,o,a,l,u=0,c=!1,d=!1,h=!0;if(typeof e!="function")throw new TypeError(dr);t=cr(t)||0,$e(n)&&(c=!!n.leading,d="maxWait"in n,s=d?Gs(cr(n.maxWait)||0,t):s,h="trailing"in n?!!n.trailing:h);function p(S){var I=r,$=i;return r=i=void 0,u=S,o=e.apply($,I),o}function g(S){return u=S,a=setTimeout(w,t),c?p(S):o}function m(S){var I=S-l,$=S-u,ee=t-I;return d?Ks(ee,s-$):ee}function v(S){var I=S-l,$=S-u;return l===void 0||I>=t||I<0||d&&$>=s}function w(){var S=Ht();if(v(S))return x(S);a=setTimeout(w,m(S))}function x(S){return a=void 0,h&&r?p(S):(r=i=void 0,o)}function P(){a!==void 0&&clearTimeout(a),u=0,r=l=i=a=void 0}function Q(){return a===void 0?o:x(Ht())}function V(){var S=Ht(),I=v(S);if(r=arguments,i=this,l=S,I){if(a===void 0)return g(l);if(d)return a=setTimeout(w,t),p(l)}return a===void 0&&(a=setTimeout(w,t)),o}return V.cancel=P,V.flush=Q,V}function Js(e,t,n){var r=!0,i=!0;if(typeof e!="function")throw new TypeError(dr);return $e(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),Ys(e,t,{leading:r,maxWait:t,trailing:i})}function $e(e){var t=typeof e;return!!e&&(t=="object"||t=="function")}function Xs(e){return!!e&&typeof e=="object"}function Zs(e){return typeof e=="symbol"||Xs(e)&&zs.call(e)==Ds}function cr(e){if(typeof e=="number")return e;if(Zs(e))return ur;if($e(e)){var t=typeof e.valueOf=="function"?e.valueOf():e;e=$e(t)?t+"":t}if(typeof e!="string")return e===0?e:+e;e=e.replace(Bs,"");var n=qs.test(e);return n||Fs.test(e)?Vs(e.slice(2),n?2:8):Qs.test(e)?ur:+e}hr.exports=Js});var q=_(H=>{"use strict";H.__esModule=!0;H.extend=Lr;H.indexOf=fo;H.escapeExpression=po;H.isEmpty=mo;H.createFrame=go;H.blockParams=vo;H.appendContextPath=yo;var lo={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`","=":"="},uo=/[&<>"'`=]/g,co=/[&<>"'`=]/;function ho(e){return lo[e]}function Lr(e){for(var t=1;t{"use strict";ze.__esModule=!0;var zt=["description","fileName","lineNumber","endLineNumber","message","name","number","stack"];function Gt(e,t){var n=t&&t.loc,r=void 0,i=void 0,s=void 0,o=void 0;n&&(r=n.start.line,i=n.end.line,s=n.start.column,o=n.end.column,e+=" - "+r+":"+s);for(var a=Error.prototype.constructor.call(this,e),l=0;l{"use strict";Ge.__esModule=!0;var Kt=q();Ge.default=function(e){e.registerHelper("blockHelperMissing",function(t,n){var r=n.inverse,i=n.fn;if(t===!0)return i(this);if(t===!1||t==null)return r(this);if(Kt.isArray(t))return t.length>0?(n.ids&&(n.ids=[n.name]),e.helpers.each(t,n)):r(this);if(n.data&&n.ids){var s=Kt.createFrame(n.data);s.contextPath=Kt.appendContextPath(n.data.contextPath,n.name),n={data:s}}return i(t,n)})};Or.exports=Ge.default});var Cr=_((Ke,Ir)=>{"use strict";Ke.__esModule=!0;function wo(e){return e&&e.__esModule?e:{default:e}}var be=q(),bo=X(),Eo=wo(bo);Ke.default=function(e){e.registerHelper("each",function(t,n){if(!n)throw new Eo.default("Must pass iterator to #each");var r=n.fn,i=n.inverse,s=0,o="",a=void 0,l=void 0;n.data&&n.ids&&(l=be.appendContextPath(n.data.contextPath,n.ids[0])+"."),be.isFunction(t)&&(t=t.call(this)),n.data&&(a=be.createFrame(n.data));function u(g,m,v){a&&(a.key=g,a.index=m,a.first=m===0,a.last=!!v,l&&(a.contextPath=l+g)),o=o+r(t[g],{data:a,blockParams:be.blockParams([t[g],g],[l+g,null])})}if(t&&typeof t=="object")if(be.isArray(t))for(var c=t.length;s{"use strict";Ye.__esModule=!0;function xo(e){return e&&e.__esModule?e:{default:e}}var ko=X(),So=xo(ko);Ye.default=function(e){e.registerHelper("helperMissing",function(){if(arguments.length!==1)throw new So.default('Missing helper: "'+arguments[arguments.length-1].name+'"')})};Rr.exports=Ye.default});var Br=_((Je,Dr)=>{"use strict";Je.__esModule=!0;function _o(e){return e&&e.__esModule?e:{default:e}}var Nr=q(),Lo=X(),Hr=_o(Lo);Je.default=function(e){e.registerHelper("if",function(t,n){if(arguments.length!=2)throw new Hr.default("#if requires exactly one argument");return Nr.isFunction(t)&&(t=t.call(this)),!n.hash.includeZero&&!t||Nr.isEmpty(t)?n.inverse(this):n.fn(this)}),e.registerHelper("unless",function(t,n){if(arguments.length!=2)throw new Hr.default("#unless requires exactly one argument");return e.helpers.if.call(this,t,{fn:n.inverse,inverse:n.fn,hash:n.hash})})};Dr.exports=Je.default});var qr=_((Xe,Qr)=>{"use strict";Xe.__esModule=!0;Xe.default=function(e){e.registerHelper("log",function(){for(var t=[void 0],n=arguments[arguments.length-1],r=0;r{"use strict";Ze.__esModule=!0;Ze.default=function(e){e.registerHelper("lookup",function(t,n,r){return t&&r.lookupProperty(t,n)})};Fr.exports=Ze.default});var Ur=_((et,$r)=>{"use strict";et.__esModule=!0;function To(e){return e&&e.__esModule?e:{default:e}}var Ee=q(),Po=X(),Oo=To(Po);et.default=function(e){e.registerHelper("with",function(t,n){if(arguments.length!=2)throw new Oo.default("#with requires exactly one argument");Ee.isFunction(t)&&(t=t.call(this));var r=n.fn;if(Ee.isEmpty(t))return n.inverse(this);var i=n.data;return n.data&&n.ids&&(i=Ee.createFrame(n.data),i.contextPath=Ee.appendContextPath(n.data.contextPath,n.ids[0])),r(t,{data:i,blockParams:Ee.blockParams([t],[i&&i.contextPath])})})};$r.exports=et.default});var Yt=_(tt=>{"use strict";tt.__esModule=!0;tt.registerDefaultHelpers=Uo;tt.moveHelperToHooks=jo;function se(e){return e&&e.__esModule?e:{default:e}}var Ao=Ar(),Io=se(Ao),Co=Cr(),Ro=se(Co),Mo=Mr(),No=se(Mo),Ho=Br(),Do=se(Ho),Bo=qr(),Qo=se(Bo),qo=Vr(),Fo=se(qo),Vo=Ur(),$o=se(Vo);function Uo(e){Io.default(e),Ro.default(e),No.default(e),Do.default(e),Qo.default(e),Fo.default(e),$o.default(e)}function jo(e,t,n){e.helpers[t]&&(e.hooks[t]=e.helpers[t],n||delete e.helpers[t])}});var Wr=_((nt,jr)=>{"use strict";nt.__esModule=!0;var Wo=q();nt.default=function(e){e.registerDecorator("inline",function(t,n,r,i){var s=t;return n.partials||(n.partials={},s=function(o,a){var l=r.partials;r.partials=Wo.extend({},l,n.partials);var u=t(o,a);return r.partials=l,u}),n.partials[i.args[0]]=i.fn,s})};jr.exports=nt.default});var zr=_(Jt=>{"use strict";Jt.__esModule=!0;Jt.registerDefaultDecorators=Yo;function zo(e){return e&&e.__esModule?e:{default:e}}var Go=Wr(),Ko=zo(Go);function Yo(e){Ko.default(e)}});var Xt=_((rt,Gr)=>{"use strict";rt.__esModule=!0;var Jo=q(),fe={methodMap:["debug","info","warn","error"],level:"info",lookupLevel:function(t){if(typeof t=="string"){var n=Jo.indexOf(fe.methodMap,t.toLowerCase());n>=0?t=n:t=parseInt(t,10)}return t},log:function(t){if(t=fe.lookupLevel(t),typeof console<"u"&&fe.lookupLevel(fe.level)<=t){var n=fe.methodMap[t];console[n]||(n="log");for(var r=arguments.length,i=Array(r>1?r-1:0),s=1;s{"use strict";Zt.__esModule=!0;Zt.createNewLookupObject=Zo;var Xo=q();function Zo(){for(var e=arguments.length,t=Array(e),n=0;n{"use strict";xe.__esModule=!0;xe.createProtoAccessControl=ra;xe.resultIsAllowed=ia;xe.resetLoggedProperties=oa;function ea(e){return e&&e.__esModule?e:{default:e}}var Yr=Kr(),ta=Xt(),na=ea(ta),it=Object.create(null);function ra(e){var t=Object.create(null);t.constructor=!1,t.__defineGetter__=!1,t.__defineSetter__=!1,t.__lookupGetter__=!1;var n=Object.create(null);return n.__proto__=!1,{properties:{whitelist:Yr.createNewLookupObject(n,e.allowedProtoProperties),defaultValue:e.allowProtoPropertiesByDefault},methods:{whitelist:Yr.createNewLookupObject(t,e.allowedProtoMethods),defaultValue:e.allowProtoMethodsByDefault}}}function ia(e,t,n){return Jr(typeof e=="function"?t.methods:t.properties,n)}function Jr(e,t){return e.whitelist[t]!==void 0?e.whitelist[t]===!0:e.defaultValue!==void 0?e.defaultValue:(sa(t),!1)}function sa(e){it[e]!==!0&&(it[e]=!0,na.default.log("error",'Handlebars: Access has been denied to resolve the property "'+e+`" because it is not an "own property" of its parent. -You can add a runtime option to disable the check or this warning: -See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details`))}function oa(){Object.keys(it).forEach(function(e){delete it[e]})}});var sn=_(U=>{"use strict";U.__esModule=!0;U.HandlebarsEnvironment=rn;function Xr(e){return e&&e.__esModule?e:{default:e}}var oe=q(),aa=X(),tn=Xr(aa),la=Yt(),ua=zr(),ca=Xt(),st=Xr(ca),da=en(),ha="4.7.8";U.VERSION=ha;var fa=8;U.COMPILER_REVISION=fa;var pa=7;U.LAST_COMPATIBLE_COMPILER_REVISION=pa;var ma={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};U.REVISION_CHANGES=ma;var nn="[object Object]";function rn(e,t,n){this.helpers=e||{},this.partials=t||{},this.decorators=n||{},la.registerDefaultHelpers(this),ua.registerDefaultDecorators(this)}rn.prototype={constructor:rn,logger:st.default,log:st.default.log,registerHelper:function(t,n){if(oe.toString.call(t)===nn){if(n)throw new tn.default("Arg not supported with multiple helpers");oe.extend(this.helpers,t)}else this.helpers[t]=n},unregisterHelper:function(t){delete this.helpers[t]},registerPartial:function(t,n){if(oe.toString.call(t)===nn)oe.extend(this.partials,t);else{if(typeof n>"u")throw new tn.default('Attempting to register a partial called "'+t+'" as undefined');this.partials[t]=n}},unregisterPartial:function(t){delete this.partials[t]},registerDecorator:function(t,n){if(oe.toString.call(t)===nn){if(n)throw new tn.default("Arg not supported with multiple decorators");oe.extend(this.decorators,t)}else this.decorators[t]=n},unregisterDecorator:function(t){delete this.decorators[t]},resetLoggedPropertyAccesses:function(){da.resetLoggedProperties()}};var ga=st.default.log;U.log=ga;U.createFrame=oe.createFrame;U.logger=st.default});var ei=_((ot,Zr)=>{"use strict";ot.__esModule=!0;function on(e){this.string=e}on.prototype.toString=on.prototype.toHTML=function(){return""+this.string};ot.default=on;Zr.exports=ot.default});var ti=_(an=>{"use strict";an.__esModule=!0;an.wrapHelper=va;function va(e,t){if(typeof e!="function")return e;var n=function(){var i=arguments[arguments.length-1];return arguments[arguments.length-1]=t(i),e.apply(this,arguments)};return n}});var oi=_(Z=>{"use strict";Z.__esModule=!0;Z.checkRevision=ka;Z.template=Sa;Z.wrapProgram=at;Z.resolvePartial=_a;Z.invokePartial=La;Z.noop=ii;function ya(e){return e&&e.__esModule?e:{default:e}}function wa(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var ba=q(),z=wa(ba),Ea=X(),G=ya(Ea),K=sn(),ni=Yt(),xa=ti(),ri=en();function ka(e){var t=e&&e[0]||1,n=K.COMPILER_REVISION;if(!(t>=K.LAST_COMPATIBLE_COMPILER_REVISION&&t<=K.COMPILER_REVISION))if(t{"use strict";lt.__esModule=!0;lt.default=function(e){(function(){typeof globalThis!="object"&&(Object.prototype.__defineGetter__("__magic__",function(){return this}),__magic__.globalThis=__magic__,delete Object.prototype.__magic__)})();var t=globalThis.Handlebars;e.noConflict=function(){return globalThis.Handlebars===e&&(globalThis.Handlebars=t),e}};ai.exports=lt.default});var fi=_((ut,hi)=>{"use strict";ut.__esModule=!0;function un(e){return e&&e.__esModule?e:{default:e}}function cn(e){if(e&&e.__esModule)return e;var t={};if(e!=null)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var Aa=sn(),ui=cn(Aa),Ia=ei(),Ca=un(Ia),Ra=X(),Ma=un(Ra),Na=q(),ln=cn(Na),Ha=oi(),ci=cn(Ha),Da=li(),Ba=un(Da);function di(){var e=new ui.HandlebarsEnvironment;return ln.extend(e,ui),e.SafeString=Ca.default,e.Exception=Ma.default,e.Utils=ln,e.escapeExpression=ln.escapeExpression,e.VM=ci,e.template=function(t){return ci.template(t,e)},e}var ke=di();ke.create=di;Ba.default(ke);ke.default=ke;ut.default=ke;hi.exports=ut.default});var ae=_((Rc,pi)=>{pi.exports=fi().default});var Ui=_((Vi,$i)=>{(function(){var e=function(t){var n=new e.Builder;return n.pipeline.add(e.trimmer,e.stopWordFilter,e.stemmer),n.searchPipeline.add(e.stemmer),t.call(n,n),n.build()};e.version="2.3.9";e.utils={},e.utils.warn=(function(t){return function(n){t.console&&console.warn&&console.warn(n)}})(this),e.utils.asString=function(t){return t==null?"":t.toString()},e.utils.clone=function(t){if(t==null)return t;for(var n=Object.create(null),r=Object.keys(t),i=0;i0){var c=e.utils.clone(n)||{};c.position=[a,u],c.index=s.length,s.push(new e.Token(r.slice(a,o),c))}a=o+1}}return s},e.tokenizer.separator=/[\s\-]+/;e.Pipeline=function(){this._stack=[]},e.Pipeline.registeredFunctions=Object.create(null),e.Pipeline.registerFunction=function(t,n){n in this.registeredFunctions&&e.utils.warn("Overwriting existing registered function: "+n),t.label=n,e.Pipeline.registeredFunctions[t.label]=t},e.Pipeline.warnIfFunctionNotRegistered=function(t){var n=t.label&&t.label in this.registeredFunctions;n||e.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. -`,t)},e.Pipeline.load=function(t){var n=new e.Pipeline;return t.forEach(function(r){var i=e.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},e.Pipeline.prototype.add=function(){var t=Array.prototype.slice.call(arguments);t.forEach(function(n){e.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},e.Pipeline.prototype.after=function(t,n){e.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(t);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},e.Pipeline.prototype.before=function(t,n){e.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(t);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},e.Pipeline.prototype.remove=function(t){var n=this._stack.indexOf(t);n!=-1&&this._stack.splice(n,1)},e.Pipeline.prototype.run=function(t){for(var n=this._stack.length,r=0;r1&&(ot&&(r=s),o!=t);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==t||o>t)return s*2;if(ol?c+=2:a==l&&(n+=r[u+1]*i[c+1],u+=2,c+=2);return n},e.Vector.prototype.similarity=function(t){return this.dot(t)/this.magnitude()||0},e.Vector.prototype.toArray=function(){for(var t=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new e.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new e.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new e.TokenSet;s.node.edges["*"]=u}s.str.length==1&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var c=s.str.charAt(0),d=s.str.charAt(1),h;d in s.node.edges?h=s.node.edges[d]:(h=new e.TokenSet,s.node.edges[d]=h),s.str.length==1&&(h.final=!0),i.push({node:h,editsRemaining:s.editsRemaining-1,str:c+s.str.slice(2)})}}}return r},e.TokenSet.fromString=function(t){for(var n=new e.TokenSet,r=n,i=0,s=t.length;i=t;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};e.Index=function(t){this.invertedIndex=t.invertedIndex,this.fieldVectors=t.fieldVectors,this.tokenSet=t.tokenSet,this.fields=t.fields,this.pipeline=t.pipeline},e.Index.prototype.search=function(t){return this.query(function(n){var r=new e.QueryParser(t,n);r.parse()})},e.Index.prototype.query=function(t){for(var n=new e.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=t},e.Builder.prototype.k1=function(t){this._k1=t},e.Builder.prototype.add=function(t,n){var r=t[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){var t,n;do t=this.next(),n=t.charCodeAt(0);while(n>47&&n<58);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos1&&(t.backup(),t.emit(e.QueryLexer.TERM)),t.ignore(),t.more())return e.QueryLexer.lexText},e.QueryLexer.lexEditDistance=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.EDIT_DISTANCE),e.QueryLexer.lexText},e.QueryLexer.lexBoost=function(t){return t.ignore(),t.acceptDigitRun(),t.emit(e.QueryLexer.BOOST),e.QueryLexer.lexText},e.QueryLexer.lexEOS=function(t){t.width()>0&&t.emit(e.QueryLexer.TERM)},e.QueryLexer.termSeparator=e.tokenizer.separator,e.QueryLexer.lexText=function(t){for(;;){var n=t.next();if(n==e.QueryLexer.EOS)return e.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){t.escapeCharacter();continue}if(n==":")return e.QueryLexer.lexField;if(n=="~")return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexEditDistance;if(n=="^")return t.backup(),t.width()>0&&t.emit(e.QueryLexer.TERM),e.QueryLexer.lexBoost;if(n=="+"&&t.width()===1||n=="-"&&t.width()===1)return t.emit(e.QueryLexer.PRESENCE),e.QueryLexer.lexText;if(n.match(e.QueryLexer.termSeparator))return e.QueryLexer.lexTerm}},e.QueryParser=function(t,n){this.lexer=new e.QueryLexer(t),this.query=n,this.currentClause={},this.lexemeIdx=0},e.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var t=e.QueryParser.parseClause;t;)t=t(this);return this.query},e.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},e.QueryParser.prototype.consumeLexeme=function(){var t=this.peekLexeme();return this.lexemeIdx+=1,t},e.QueryParser.prototype.nextClause=function(){var t=this.currentClause;this.query.clause(t),this.currentClause={}},e.QueryParser.parseClause=function(t){var n=t.peekLexeme();if(n!=null)switch(n.type){case e.QueryLexer.PRESENCE:return e.QueryParser.parsePresence;case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new e.QueryParseError(r,n.start,n.end)}},e.QueryParser.parsePresence=function(t){var n=t.consumeLexeme();if(n!=null){switch(n.str){case"-":t.currentClause.presence=e.Query.presence.PROHIBITED;break;case"+":t.currentClause.presence=e.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new e.QueryParseError(r,n.start,n.end)}var i=t.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new e.QueryParseError(r,n.start,n.end)}switch(i.type){case e.QueryLexer.FIELD:return e.QueryParser.parseField;case e.QueryLexer.TERM:return e.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new e.QueryParseError(r,i.start,i.end)}}},e.QueryParser.parseField=function(t){var n=t.consumeLexeme();if(n!=null){if(t.query.allFields.indexOf(n.str)==-1){var r=t.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.fields=[n.str];var s=t.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new e.QueryParseError(i,n.start,n.end)}if(s.type===e.QueryLexer.TERM)return e.QueryParser.parseTerm;var i="expecting term, found '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}},e.QueryParser.parseTerm=function(t){var n=t.consumeLexeme();if(n!=null){t.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(t.currentClause.usePipeline=!1);var r=t.peekLexeme();if(r==null){t.nextClause();return}switch(r.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new e.QueryParseError(i,r.start,r.end)}}},e.QueryParser.parseEditDistance=function(t){var n=t.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.editDistance=r;var s=t.peekLexeme();if(s==null){t.nextClause();return}switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}}},e.QueryParser.parseBoost=function(t){var n=t.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new e.QueryParseError(i,n.start,n.end)}t.currentClause.boost=r;var s=t.peekLexeme();if(s==null){t.nextClause();return}switch(s.type){case e.QueryLexer.TERM:return t.nextClause(),e.QueryParser.parseTerm;case e.QueryLexer.FIELD:return t.nextClause(),e.QueryParser.parseField;case e.QueryLexer.EDIT_DISTANCE:return e.QueryParser.parseEditDistance;case e.QueryLexer.BOOST:return e.QueryParser.parseBoost;case e.QueryLexer.PRESENCE:return t.nextClause(),e.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new e.QueryParseError(i,s.start,s.end)}}},(function(t,n){typeof define=="function"&&define.amd?define(n):typeof Vi=="object"?$i.exports=n():t.lunr=n()})(this,function(){return e})})()});var ie={plain:"plain",function:"function",module:"module"},Is=[{href:"typespecs.html#basic-types",hint:{kind:ie.plain,description:"Basic type"}},{href:"typespecs.html#literals",hint:{kind:ie.plain,description:"Literal"}},{href:"typespecs.html#built-in-types",hint:{kind:ie.plain,description:"Built-in type"}}],Me={cancelHintFetching:null};function Wn(e){if(Gn(e))return!0;let t=/#.*\//;return e.includes("#")&&!t.test(e)?!1:e.includes(".html")}function zn(e){let t=Gn(e);return t?Promise.resolve(t):Cs(e)}function Gn(e){let t=Is.find(n=>e.includes(n.href));return t?t.hint:null}function Cs(e){let t=e.replace(".html",".html?hint=true");return new Promise((n,r)=>{let i=document.createElement("iframe");i.setAttribute("src",t),i.style.display="none";function s(a){let{href:l,hint:u}=a.data;t===l&&(o(),n(u))}Me.cancelHintFetching=()=>{o(),r(new Error("cancelled"))};function o(){i.remove(),window.removeEventListener("message",s),Me.cancelHintFetching=null}window.addEventListener("message",s),document.body.appendChild(i)})}function Kn(){Me.cancelHintFetching&&Me.cancelHintFetching()}function Yn(e){let n=e.querySelector("h1").textContent,r=e.querySelector(".docstring > p"),i=r?r.innerHTML:"";return{kind:ie.function,title:n.trim(),description:i.trim()}}function Jn(e){let n=e.querySelector("h1 > span").textContent,r=e.querySelector("#moduledoc p"),i=r?r.innerHTML:"";return{kind:ie.module,title:n.trim(),description:i.trim()}}var f=document.querySelector.bind(document),A=document.querySelectorAll.bind(document);function Xn(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function He(e){return String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function De(){return document.getElementById("main").dataset.type}var Ne=["H1","H2","H3","H4","H5","H6"];function Zn(e=!1){let t=window.location.hash.replace(/^#/,"");if(!t)return e?document.getElementById("top-content"):null;let n=document.getElementById(t);if(!n)return null;if(n.matches(".detail"))return n;if(Ne.includes(n.tagName))return Tt(n);let r=Rs(n);return r||document.getElementById("top-content")}function Rs(e){let t=e.previousElementSibling;for(;t;){if(Ne.includes(t.tagName))return Tt(t);t=t.previousElementSibling}let n=e.parentNode;for(;n;){for(t=n.previousElementSibling;t;){if(Ne.includes(t.tagName))return Tt(t);t=t.previousElementSibling}n=n.parentNode}return null}function Tt(e){let t=document.createElement("div"),n=[e],r=e;for(;(r=r.nextSibling)&&!(Ne.includes(r.tagName)&&r.tagName<=e.tagName);)n.push(r);return t.append(...n),t}function er(e){return new URLSearchParams(window.location.search).get(e)}function Pt(e){return fetch(e).then(t=>t.ok).catch(()=>!1)}function he(e){return!e||e.trim()===""}function tr(e,t){let n;return function(...i){clearTimeout(n),n=setTimeout(()=>{n=null,e(...i)},t)}}function Be(){return document.head.querySelector("meta[name=project][content]").content}function Qe(){return document.documentElement.classList.contains("apple-os")}function E(e,t,n){let r=document.createElement(e);for(let i in t)t[i]!=null&&r.setAttribute(i,t[i]);return n&&r.replaceChildren(...n),r}var Ms=new URLSearchParams(window.location.search),Ns=window.self!==window.parent,Ot=Ns&&Ms.has("hint"),C=Ot;function qe(){return window.sidebarNodes||{}}function nr(){return window.versionNodes||[]}if(Ot&&C){let e=Zn(),t=e?Yn(e):["modules","tasks"].includes(De())?Jn(f(".content-inner")):null;if(t){let n={hint:{...t,version:Be()},href:window.location.href};window.parent.postMessage(n,"*")}f(".content-inner")?.replaceChildren()}var At="ex_doc:settings",rr="dark",It="system",Ct="dark",Rt="light";var Hs={tooltips:!0,theme:null,livebookUrl:null},Mt=class{constructor(){this._subscribers=[],this._settings=Hs,this._loadSettings()}get(){return this._settings}update(t){let n=this._settings;this._settings={...this._settings,...t},this._subscribers.forEach(r=>r(this._settings,n)),this._storeSettings()}getAndSubscribe(t){this._subscribers.push(t),t(this._settings)}_loadSettings(){try{let t=localStorage.getItem(At);if(t){let n=JSON.parse(t);this._settings={...this._settings,...n}}this._loadSettingsLegacy()}catch(t){console.error(`Failed to load settings: ${t}`)}}_storeSettings(){try{this._storeSettingsLegacy(),localStorage.setItem(At,JSON.stringify(this._settings))}catch(t){console.error(`Failed to persist settings: ${t}`)}}_loadSettingsLegacy(){localStorage.getItem("tooltipsDisabled")!==null&&(this._settings={...this._settings,tooltips:!1}),localStorage.getItem("night-mode")==="true"&&(this._settings={...this._settings,nightMode:!0}),this._settings.nightMode===!0&&(this._settings={...this._settings,theme:"dark"})}_storeSettingsLegacy(){this._settings.tooltips?localStorage.removeItem("tooltipsDisabled"):localStorage.setItem("tooltipsDisabled","true"),this._settings.nightMode!==null?localStorage.setItem("night-mode",this._settings.nightMode===!0?"true":"false"):localStorage.removeItem("night-mode"),this._settings.theme!==null?(localStorage.setItem("night-mode",this._settings.theme==="dark"?"true":"false"),this._settings.nightMode=this._settings.theme==="dark"):(delete this._settings.nightMode,localStorage.removeItem("night-mode"))}},R=new Mt;var ir=!1,Fe=null,J=null;function Ve(e){ir||(ir=!0,J=document.getElementById("toast"),J?.addEventListener("click",()=>{clearTimeout(Fe),J.classList.remove("show")})),J&&(clearTimeout(Fe),J.innerText=e,J.classList.add("show"),Fe=setTimeout(()=>{J.classList.remove("show"),Fe=setTimeout(function(){J.innerText=""},1e3)},5e3))}var Nt=[It,Ct,Rt],sr=window.matchMedia("(prefers-color-scheme: dark)");R.getAndSubscribe(or);sr.addEventListener("change",or);function or(){let e=lr(),t=e===Ct||e!==Rt&&sr.matches;document.body.classList.toggle(rr,t)}function ar(){let e=Nt[Nt.indexOf(lr())+1]||Nt[0];R.update({theme:e}),Ve(`Set theme to "${e}"`)}function lr(){return new URLSearchParams(window.location.search).get("theme")||R.get().theme||It}var Er=Y(fr());var Dt="sidebar_state",Bt="closed",pr="open",mr="sidebar_width";var Ue="sidebar-open",je="sidebar-transition";var gr=!1;function vr(){if(gr)return;gr=!0;let e=document.getElementById("sidebar-list-nav");if(!e)return;let t=De(),n={extras:e.dataset.extras,modules:"Modules",tasks:'Mix Tasks'};Object.entries(n).forEach(([r,i])=>{let s=qe()[r];if(!s?.length)return;let o=`${r}-list-tab-button`,a=`${r}-tab-panel`,l=r===t,u=E("button",{id:o,role:"tab",tabindex:l?0:-1,"aria-selected":l||void 0,"aria-controls":a});u.innerHTML=i,u.addEventListener("keydown",no),u.addEventListener("click",ro),e.appendChild(E("li",{},[u]));let c=E("ul",{class:"full-list"});c.addEventListener("click",io);let d=E("div",{id:a,class:"sidebar-tabpanel",role:"tabpanel","aria-labelledby":o,hidden:l?void 0:""},[c]);document.getElementById("sidebar").appendChild(d);let h="",p,g;c.replaceChildren(...s.flatMap(m=>{let v=[],w=Array.isArray(m.headers),x=w?void 0:"no",P=m?.url||`${m.id}.html`;return m.group!==h&&(v.push(E("li",{class:"group",translate:x},[m.group])),h=m.group,p=void 0),m.nested_context&&m.nested_context!==p?(p=m.nested_context,g!==p&&v.push(E("li",{class:"nesting-context",translate:"no","aria-hidden":!0},[p]))):g=m.title,v.push(E("li",{},[E("a",{href:P,translate:x,...m.url?{rel:"nofollow"}:{}},[m.nested_title||m.title,m.url?E("i",{class:"external-link ri-external-link-line"}):null].filter(Boolean)),...qt(`node-${m.id}-headers`,w?eo(m):to(m))])),v}))}),Qt(),requestAnimationFrame(yr),window.addEventListener("hashchange",Qt),window.addEventListener("exdoc:loaded",Qt)}function qt(e,t){return t.length?[E("button",{"aria-label":"expand","aria-expanded":!1,"aria-controls":e}),E("ul",{id:e},t)]:[]}function eo(e){return e.headers.map(({id:t,anchor:n})=>E("li",{},[E("a",{href:`${e.id}.html#${n}`},[t])]))}function to(e){let t=[];return e.sections?.length&&t.push(E("li",{},[E("a",{href:`${e.id}.html#content`},["Sections"]),...qt(`${e.id}-sections-list`,e.sections.map(({id:n,anchor:r})=>E("li",{},[E("a",{href:`${e.id}.html#${r}`},[n])])))])),e.nodeGroups&&(t.push(E("li",{},[E("a",{href:`${e.id}.html#summary`},["Summary"])])),t.push(...e.nodeGroups.map(({key:n,name:r,nodes:i})=>E("li",{},[E("a",{href:`${e.id}.html#${n}`},[r]),...qt(`node-${e.id}-group-${n}-list`,i.map(({anchor:s,title:o,id:a})=>E("li",{},[E("a",{href:`${e.id}.html#${s}`,title:o,translate:"no"},[a])])))])))),t}function Ft(e){let t=document.getElementById("sidebar-list-nav").querySelector("[aria-selected]");t!==e&&(t&&(t.removeAttribute("aria-selected"),t.setAttribute("tabindex","-1"),document.getElementById(t.getAttribute("aria-controls")).setAttribute("hidden","hidden")),e.setAttribute("aria-selected","true"),e.setAttribute("tabindex","0"),document.getElementById(e.getAttribute("aria-controls")).removeAttribute("hidden"))}function yr(){f("#sidebar [role=tabpanel]:not([hidden]) a[aria-selected]")?.scrollIntoView()}function Qt(){let e=document.getElementById("sidebar"),{pathname:t,hash:n}=window.location,r=t.split("/").pop().replace(/\.html$/,"")+".html",i=e.querySelector(`li a[href="${r+n}"]`)||e.querySelector(`li a[href="${r}"]`);if(!i)return;e.querySelectorAll(".full-list a[aria-selected]").forEach(o=>{o.removeAttribute("aria-selected")}),e.querySelectorAll(".full-list button[aria-expanded=true]").forEach(o=>{o.setAttribute("aria-expanded",!1)});let s=i.parentElement;for(;s;){if(s.tagName==="LI"){let o=s.firstChild;o.setAttribute("aria-selected",o.getAttribute("href")===r?"page":"true");let a=o.nextSibling;a?.tagName==="BUTTON"&&a.setAttribute("aria-expanded",!0)}else if(s.role==="tabpanel"){s.hasAttribute("hidden")&&Ft(document.getElementById(s.getAttribute("aria-labelledby")));break}s=s.parentElement}}function no(e){if(!["ArrowRight","ArrowLeft"].includes(e.key))return;let t=Array.from(A('#sidebar-list-nav [role="tab"]')),r=t.indexOf(e.currentTarget)+(e.key==="ArrowRight"?1:-1),i=t.at(r%t.length);Ft(i),i.focus()}function ro(e){Ft(e.currentTarget),yr()}function io(e){let t=e.target;t.tagName==="BUTTON"&&t.setAttribute("aria-expanded",t.getAttribute("aria-expanded")==="false")}var oo=300,xr=".sidebar-toggle",Vt=window.matchMedia(`screen and (max-width: ${768}px)`);if(!C){window.addEventListener("exdoc:loaded",wr);let e=document.getElementById("sidebar"),t=f(xr);t.addEventListener("click",We),document.body.addEventListener("click",i=>{Vt.matches&&$t()&&!e.contains(i.target)&&!t.contains(i.target)&&We()});let n=window.innerWidth;window.addEventListener("resize",(0,Er.default)(()=>{n!==window.innerWidth&&(n=window.innerWidth,wr())},100));let r=new ResizeObserver(([i])=>{if(!i)return;let s=i.contentRect.width;sessionStorage.setItem(mr,s),document.body.style.setProperty("--sidebarWidth",`${s}px`)});e.addEventListener("mousedown",()=>r.observe(e)),e.addEventListener("mouseup",()=>r.unobserve(e)),window.addEventListener("hashchange",ao)}function wr(){let t=sessionStorage.getItem(Dt)!==Bt&&!Vt.matches;Sr(t)}function We(){let e=!$t();return sessionStorage.setItem(Dt,e?pr:Bt),Ut(e)}function $t(){return document.body.classList.contains(Ue)}function kr(){return document.body.classList.contains(Ue)&&!document.body.classList.contains(je)}function Sr(e){e&&vr(),document.body.classList.toggle(Ue,e),f(xr).setAttribute("aria-expanded",e?"true":"false")}var br;function Ut(e){return new Promise(t=>{document.body.classList.add(je),document.body.scrollTop,Sr(e),clearTimeout(br),br=setTimeout(()=>{document.body.classList.remove(je),t()},oo)})}function _r(){return Ut(!0)}function ao(){Vt.matches&&$t()&&Ut(!1)}var mi=Y(ae());var dn=Y(ae());dn.registerHelper("isArray",function(e,t){return Array.isArray(e)?t.fn(this):t.inverse(this)});dn.registerHelper("isNonEmptyArray",function(e,t){return Array.isArray(e)&&e.length>0?t.fn(this):t.inverse(this)});var gi=mi.template({1:function(e,t,n,r,i){var s,o,a=t??(e.nullContext||{}),l=e.hooks.helperMissing,u="function",c=e.escapeExpression,d=e.lookupProperty||function(h,p){if(Object.prototype.hasOwnProperty.call(h,p))return h[p]};return' -`},2:function(e,t,n,r,i){return" selected disabled"},4:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return` -`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`
- -`+((s=a(n,"if").call(o,t!=null?a(t,"latestVersion"):t,{name:"if",hash:{},fn:e.program(4,i,0),inverse:e.noop,data:i,loc:{start:{line:13,column:2},end:{line:19,column:9}}}))!=null?s:"")+`
-`},useData:!0});var Qa=".sidebar-projectVersion",vi=".sidebar-projectVersion select",qa=".sidebar-staleVersion a";if(!C){let e=nr(),t=f(Qa);if(e.length>0||!t){let n=t.textContent.trim(),i=(e.some(u=>u.version===n)?e:[{version:n,url:"#"},...e]).map(u=>({...u,isCurrentVersion:u.version===n})),s=e.find(u=>u.latest),o=s?.version!==n?s?.url:null;n.includes("-")&&e.findIndex(u=>u.version===n)u.latest)&&(o=null),t.innerHTML=gi({nodes:i,latestVersion:o});let a=f(vi);a.addEventListener("change",Va),Fa(a);let l=f(qa);l&&l.addEventListener("click",$a)}}function Fa(e){let t=document.createElement("span");t.style.visibility="hidden",t.style.position="absolute",t.style.whiteSpace="nowrap",t.style.font=window.getComputedStyle(e).font,t.textContent=e.options[e.selectedIndex].text,document.body.appendChild(t),e.style.width=`${t.offsetWidth+20}px`,document.body.removeChild(t)}function Va(e){let t=e.target.value,n=window.location.pathname.split("/").pop()+window.location.hash,r=`${t}/${n}`;Pt(r).then(i=>{i?window.location.href=r:window.location.href=t})}function $a(e){let t=this.href,n=window.location.pathname.split("/").pop()+window.location.hash,r=`${t}/${n}`;e.preventDefault(),Pt(r).then(i=>{i?window.location.href=r:window.location.href=t})}function hn(){let e=f(vi);e&&(e.focus(),e.addEventListener("keydown",t=>{(t.key==="Escape"||t.key==="v")&&(t.preventDefault(),e.blur())}),navigator.userActivation.isActive&&"showPicker"in HTMLSelectElement.prototype&&e.showPicker())}var Ua="content",ja="tabs-open",Wa="tabs-close",za="H3",Ga="tabset";window.addEventListener("exdoc:loaded",Ka);function Ka(){let e=[],t=[],n=document.createNodeIterator(document.getElementById(Ua),NodeFilter.SHOW_COMMENT,i=>i.nodeValue.trim()===ja?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT),r;for(;r=n.nextNode();){let i=[];e.push([r,i]);let s,o=r;for(;o=o.nextSibling;)if(o.nodeName===za){s=[];let a=o.querySelector(".text")?.childNodes||o.childNodes;i.push([a,s]),t.push(o)}else if(o.nodeName==="#comment"&&o.nodeValue.trim()===Wa){t.push(o);break}else s&&s.push(o)}e.forEach(([i,s],o)=>{let a=E("div",{class:Ga});i.parentNode.replaceChild(a,i);let l=E("div",{role:"tablist",class:"tabset-tablist"});a.appendChild(l),s.forEach(([u,c],d)=>{let h=d===0,p=`tab-${o}-${d}`,g=`tabpanel-${o}-${d}`,m=E("button",{role:"tab",id:p,class:"tabset-tab",tabindex:h?0:-1,"aria-selected":h,"aria-controls":g},u);m.addEventListener("click",Ya),m.addEventListener("keydown",Ja),l.appendChild(m);let v=E("div",{role:"tabpanel",id:g,class:"tabset-panel",hidden:h?void 0:"","aria-labelledby":p},c);a.appendChild(v)})}),t.forEach(i=>{i.parentNode.removeChild(i)})}function Ya(e){wi(e.currentTarget)}function Ja(e){if(yi[e.code]){e.preventDefault();let t=[...e.currentTarget.parentNode.childNodes],n=t.indexOf(e.currentTarget),r=yi[e.code](n,t.length);wi(t.at(r%t.length))}}var yi={ArrowLeft:e=>e-1,ArrowRight:e=>e+1,Home:()=>0,End:(e,t)=>t-1};function wi(e){let t=e.parentNode.querySelector("[aria-selected=true]");if(t===e)return;t.setAttribute("aria-selected","false"),t.tabIndex=-1,e.setAttribute("aria-selected","true"),e.tabIndex=0,e.focus();let n=document.getElementById(t.getAttribute("aria-controls"));n.setAttribute("hidden",""),n.tabIndex=-1;let r=document.getElementById(e.getAttribute("aria-controls"));r.removeAttribute("hidden"),r.tabIndex=0}window.addEventListener("exdoc:loaded",Xa);function Xa(){let e=window.location.pathname.replace(/(\.html)?$/,".livemd"),t=encodeURIComponent(new URL(e,window.location.href).toString());R.getAndSubscribe(({livebookUrl:n})=>{let r=n?`${n}/import?url=${t}`:`https://livebook.dev/run?url=${t}`;for(let i of A(".livebook-badge"))i.href=r})}var Za="hll";window.addEventListener("exdoc:loaded",el);function el(){A("[data-group-id]").forEach(e=>{e.addEventListener("mouseenter",bi),e.addEventListener("mouseleave",bi)})}function bi(e){let t=e.currentTarget,n=e.type==="mouseenter",r=t.getAttribute("data-group-id");t.parentElement.querySelectorAll(`[data-group-id="${r}"]`).forEach(i=>{i.classList.toggle(Za,n)})}var j={module:"module",moduleChild:"module-child",mixTask:"mix-task",extra:"extra",section:"section"},tl=10;function xi(e,t=null){let n=t||window.autocompleteLimit||tl;if(he(e))return[];let r=qe(),i=[...fn(r.modules,e,j.module,"module"),...nl(r.modules,e,j.moduleChild),...fn(r.tasks,e,j.mixTask,"mix task"),...fn(r.extras,e,j.extra,"page"),...pn(r.modules,e,j.section,"module"),...pn(r.tasks,e,j.section,"mix task"),...pn(r.extras,e,j.section,"page")].filter(s=>s!==null);return ul(i).slice(0,n)}function fn(e,t,n,r){return e.map(i=>i.searchData?null:il(i,t,n,r))}function nl(e,t,n){return e.filter(r=>r.nodeGroups).flatMap(r=>r.nodeGroups.flatMap(({key:i,nodes:s})=>{let o=ll(i);return s.map(a=>sl(a,r.id,t,n,o)||al(a,r.id,t,n,o))}))}function pn(e,t,n,r){return e.flatMap(i=>rl(i).map(s=>ol(i,s,t,n,r)))}function rl(e){return e.searchData?e.searchData:(e.sections||[]).concat(e.headers||[])}function il(e,t,n,r){return dt(e.title,t)?{link:`${e.id}.html`,title:pt(e.title,t),description:null,matchQuality:ht(e.title,t),deprecated:e.deprecated,labels:[r],category:n}:null}function sl(e,t,n,r,i){return dt(e.id,n)?{link:`${t}.html#${e.anchor}`,title:pt(e.id,n),labels:[i],description:t,matchQuality:ht(e.id,n),deprecated:e.deprecated,category:r}:null}function ol(e,t,n,r,i){if(!ki(t.id,n))return null;let s;return t.anchor===""?s=`${e.id}.html`:s=`${e.id}.html#${t.anchor}`,{link:s,title:pt(t.id,n),description:e.title,matchQuality:ht(t.id,n),labels:t.labels||[i,"section"],category:r}}function al(e,t,n,r,i){let s=`${t}.${e.id}`,o=`${t}:${e.id}`,a,l;if(dt(s,n))a=s,l=/\./g;else if(dt(o,n))a=o,l=/:/g;else return null;let u=n.replace(l," ");return ki(e.id,u)?{link:`${t}.html#${e.anchor}`,title:pt(e.id,u),label:i,description:t,matchQuality:ht(a,n),deprecated:e.deprecated,category:r}:null}function ll(e){switch(e){case"callbacks":return"callback";case"types":return"type";default:return"function"}}function ul(e){return e.slice().sort((t,n)=>t.matchQuality!==n.matchQuality?n.matchQuality-t.matchQuality:Ei(t.category)-Ei(n.category))}function Ei(e){switch(e){case j.module:return 1;case j.moduleChild:return 2;case j.mixTask:return 3;default:return 4}}function ki(e,t){return ft(t).some(r=>Si(e,r))}function dt(e,t){return ft(t).every(r=>Si(e,r))}function Si(e,t){return e.toLowerCase().includes(t.toLowerCase())}function ht(e,t){let n=ft(t),i=n.map(o=>o.length).reduce((o,a)=>o+a,0)/e.length,s=cl(e,n[0])?1:0;return i+s}function cl(e,t){return e.toLowerCase().startsWith(t.toLowerCase())}function ft(e){return e.trim().split(/\s+/)}function pt(e,t){let n=ft(t).sort((r,i)=>i.length-r.length);return ct(e,n)}function ct(e,t){if(t.length===0)return e;let[n,...r]=t,i=e.match(new RegExp(`(.*)(${Xn(n)})(.*)`,"i"));if(i){let[,s,o,a]=i;return ct(s,t)+""+He(o)+""+ct(a,t)}else return ct(e,r)}var _i=Y(ae());var Li=_i.template({1:function(e,t,n,r,i){return` Autocompletion results within this project -`},3:function(e,t,n,r,i){return` No autocompletion results within this project -`},5:function(e,t,n,r,i){var s,o,a=t??(e.nullContext||{}),l=e.hooks.helperMissing,u="function",c=e.escapeExpression,d=e.lookupProperty||function(h,p){if(Object.prototype.hasOwnProperty.call(h,p))return h[p]};return' - `+((s=(o=(o=d(n,"title")||(t!=null?d(t,"title"):t))!=null?o:l,typeof o===u?o.call(a,{name:"title",hash:{},data:i,loc:{start:{line:19,column:44},end:{line:19,column:55}}}):o))!=null?s:"")+` -`+((s=d(n,"if").call(a,t!=null?d(t,"description"):t,{name:"if",hash:{},fn:e.program(6,i,0),inverse:e.noop,data:i,loc:{start:{line:20,column:8},end:{line:23,column:15}}}))!=null?s:"")+((s=d(n,"if").call(a,t!=null?d(t,"deprecated"):t,{name:"if",hash:{},fn:e.program(8,i,0),inverse:e.noop,data:i,loc:{start:{line:24,column:8},end:{line:26,column:15}}}))!=null?s:"")+((s=d(n,"each").call(a,t!=null?d(t,"labels"):t,{name:"each",hash:{},fn:e.program(10,i,0),inverse:e.noop,data:i,loc:{start:{line:27,column:8},end:{line:29,column:17}}}))!=null?s:"")+` -`},6:function(e,t,n,r,i){var s,o,a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return` - `+((s=(o=(o=a(n,"description")||(t!=null?a(t,"description"):t))!=null?o:e.hooks.helperMissing,typeof o=="function"?o.call(t??(e.nullContext||{}),{name:"description",hash:{},data:i,loc:{start:{line:22,column:49},end:{line:22,column:66}}}):o))!=null?s:"")+` -`},8:function(e,t,n,r,i){return` deprecated -`},10:function(e,t,n,r,i){return' '+e.escapeExpression(e.lambda(t,t))+` -`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`
-
-
-
- -`+((s=a(n,"if").call(o,(s=t!=null?a(t,"suggestions"):t)!=null?a(s,"length"):s,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:6,column:8},end:{line:10,column:15}}}))!=null?s:"")+` - - Press RETURN for full-text search - -
-
-`+((s=a(n,"each").call(o,t!=null?a(t,"suggestions"):t,{name:"each",hash:{},fn:e.program(5,i,0),inverse:e.noop,data:i,loc:{start:{line:17,column:6},end:{line:31,column:15}}}))!=null?s:"")+`
-
-
-`},useData:!0});var pe=".autocomplete",mn=".autocomplete-suggestions",Ti=".autocomplete-suggestion",W={autocompleteSuggestions:[],selectedIdx:-1};function dl(){f(pe).classList.add("shown")}function gn(){f(pe).classList.remove("shown")}function Pi(){return f(pe).classList.contains("shown")}function vn(e){W.autocompleteSuggestions=xi(e),W.selectedIdx=-1,he(e)?gn():(hl({term:e,suggestions:W.autocompleteSuggestions}),mt(0),dl())}function hl({term:e,suggestions:t}){let n=Li({suggestions:t,term:e}),r=f(pe);r.innerHTML=n}function Oi(){return W.selectedIdx===-1?null:W.autocompleteSuggestions[W.selectedIdx]}function mt(e){fl(pl(e))}function fl(e){W.selectedIdx=e;let t=f(mn),n=f(`${Ti}.selected`),r=f(`${Ti}[data-index="${W.selectedIdx}"]`);n&&n.classList.remove("selected"),r?(r.classList.add("selected"),r.scrollIntoView({block:"nearest"})):t&&(t.scrollTop=0)}function pl(e){let t=W.autocompleteSuggestions.length+1;return(W.selectedIdx+e+1+t)%t-1}var me="form.search-bar input",ml="form.search-bar .search-close-button",gl=".engine-selector",_e=".engine-button",Le=".engine-dropdown",yt=null;C||window.addEventListener("exdoc:loaded",vl);function vl(){yl(),El(),bl()}function yl(){let e=document.querySelectorAll(".engine-option");if(e.length===0){yt="search.html?q=";return}let t=window.location.pathname;if(t.endsWith("/search.html")||t.endsWith("/search")){let n=Array.from(e).find(r=>r.dataset.engineUrl.startsWith("search.html"));if(n){Ri(n);return}}yt=e[0].dataset.engineUrl}function Ri(e){yt=e.dataset.engineUrl;let n=f(_e)?.querySelector(".engine-name");n&&(n.textContent=e.querySelector(".name").textContent),f(Le)?.querySelectorAll(".engine-option").forEach(i=>{i.setAttribute("aria-checked",i===e?"true":"false")})}function Ai(){let e=f(_e),t=f(Le);if(!e||!t)return;e.getAttribute("aria-expanded")==="true"?Se():wl()}function wl(){let e=f(_e),t=f(Le);if(!e||!t)return;e.setAttribute("aria-expanded","true"),t.removeAttribute("hidden"),t.querySelector(".engine-option")?.focus()}function Se(){let e=f(_e),t=f(Le);!e||!t||(e.setAttribute("aria-expanded","false"),t.setAttribute("hidden",""))}function Mi(e){let t=f(me);t.value=e}function Ni(){let e=f(me);document.body.classList.add("search-focused"),e.focus()}function bl(){let e=f(me),t=parseInt(document.querySelector('meta[name="exdoc:autocomplete-limit"]')?.content);t&&(window.autocompleteLimit=t),e.setAttribute("autocomplete-limit",t)}function El(){let e=f(me);if(document.querySelector('meta[name="exdoc:autocomplete"][content="off"]'))return e.addEventListener("keydown",r=>{r.key==="Enter"&&Ii(r)}),!0;e.addEventListener("keydown",r=>{let i=Qe();r.key==="Escape"?(gt(),e.blur()):r.key==="Enter"?Ii(r):r.key==="ArrowUp"||i&&r.ctrlKey&&r.key==="p"?(mt(-1),r.preventDefault()):(r.key==="ArrowDown"||i&&r.ctrlKey&&r.key==="n")&&(mt(1),r.preventDefault())}),e.addEventListener("input",r=>{vn(r.target.value)}),e.addEventListener("focus",r=>{document.body.classList.contains("search-focused")||(document.body.classList.add("search-focused"),vn(r.target.value))}),e.addEventListener("blur",r=>{let i=r.relatedTarget,s=f(mn);if(i&&s&&s.contains(i))return setTimeout(()=>{Pi()&&e.focus()},1e3),null;vt()}),f(pe).addEventListener("click",r=>{r.shiftKey||r.ctrlKey?e.focus():(gt(),vt())}),f(ml).addEventListener("click",r=>{gt(),vt(),e.blur()});let t=f(_e);t&&(t.addEventListener("click",r=>{r.stopPropagation(),Ai()}),t.addEventListener("keydown",r=>{r.key==="Enter"||r.key===" "?(r.preventDefault(),Ai()):r.key==="Escape"&&Se()})),document.addEventListener("click",r=>{let i=f(gl);i&&!i.contains(r.target)&&Se()});let n=f(Le);n&&(n.querySelectorAll(".engine-option").forEach(r=>{r.addEventListener("click",()=>{Ri(r),Se()})}),n.addEventListener("keydown",r=>{let i=Array.from(n.querySelectorAll(".engine-option")),s=i.indexOf(document.activeElement);if(r.key==="ArrowDown"){r.preventDefault();let o=(s+1)%i.length;i[o]?.focus()}else if(r.key==="ArrowUp"){r.preventDefault();let o=(s-1+i.length)%i.length;i[o]?.focus()}else r.key==="Escape"?(r.preventDefault(),Se(),t?.focus()):(r.key==="Enter"||r.key===" ")&&(r.preventDefault(),document.activeElement?.click())}))}function Ii(e){let t=f(me),n=e.shiftKey||e.ctrlKey,r=Oi();e.preventDefault();let i=n?"_blank":"_self",s=document.createElement("a");s.setAttribute("target",i),r?s.setAttribute("href",r.link):s.setAttribute("href",`${yt}${encodeURIComponent(t.value)}`),s.click(),n||(gt(),vt())}function gt(){let e=f(me);e.value=""}function vt(){document.body.classList.remove("search-focused"),gn()}var yn,Ci=2;window.addEventListener("scroll",function(){let e=window.scrollY;if(yn!==void 0){let t=e-yn;e===0||t>Ci?document.body.classList.remove("scroll-sticky"):e>0&&-t>Ci&&document.body.classList.add("scroll-sticky")}yn=Math.max(0,e)},!1);var Hi=Y(ae());var Di=Hi.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
- `+e.escapeExpression(e.lambda((s=t!=null?o(t,"hint"):t)!=null?o(s,"description"):s,t))+` -
-`},3:function(e,t,n,r,i){var s,o=e.lambda,a=e.escapeExpression,l=e.lookupProperty||function(u,c){if(Object.prototype.hasOwnProperty.call(u,c))return u[c]};return`
-

- `+a(o((s=t!=null?l(t,"hint"):t)!=null?l(s,"title"):s,t))+` -
`+a(o((s=t!=null?l(t,"hint"):t)!=null?l(s,"version"):s,t))+`
-

-
-`+((s=l(n,"if").call(t??(e.nullContext||{}),(s=t!=null?l(t,"hint"):t)!=null?l(s,"description"):s,{name:"if",hash:{},fn:e.program(4,i,0),inverse:e.noop,data:i,loc:{start:{line:12,column:2},end:{line:16,column:9}}}))!=null?s:"")},4:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
- `+((s=e.lambda((s=t!=null?o(t,"hint"):t)!=null?o(s,"description"):s,t))!=null?s:"")+` -
-`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"isPlain"):t,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:1,column:0},end:{line:17,column:7}}}))!=null?s:""},useData:!0});var xl='
',kl='.content a:not([data-no-tooltip=""])',wn=".tooltip",Bi=".tooltip .tooltip-body",Qi="body .content-inner",Sl="#content",qi="tooltip-shown",Te=10,_l=Te*4,Ll=768,Tl=450,Pl=100,le={currentLinkElement:null,hoverDelayTimeout:null};window.addEventListener("exdoc:loaded",Ol);function Ol(){A(kl).forEach(e=>{Al(e)&&(e.addEventListener("mouseenter",Cl),e.addEventListener("mouseleave",Ml))})}function Al(e){return!(Il(e.href)||!Wn(e.href))}function Il(e){let t=e.replace(Sl,"");return window.location.href.split("#")[0]===t}function Cl(e){if(window.innerWidth{zn(t.href).then(Rl).catch(()=>{})},Pl)}function Rl(e){let t=Di({isPlain:e.kind===ie.plain,hint:e}),n=f(Bi);n||(f(Qi).insertAdjacentHTML("beforeend",xl),n=f(Bi)),n.innerHTML=t,Nl(),f(wn).classList.add(qi)}function Ml(){le.currentLinkElement&&(clearTimeout(le.hoverDelayTimeout),Kn(),le.currentLinkElement=null,f(wn)?.classList.remove(qi))}function Nl(){if(!le.currentLinkElement)return;let e=f(wn),t=le.currentLinkElement.getBoundingClientRect(),n=f(Qi).getBoundingClientRect(),r=e.getBoundingClientRect(),i=Hl(t,n);if(t.left+r.width+Te code:first-child):not(:has(.copy-button))").forEach(e=>{if(!bn){let r=document.createElement("div");r.innerHTML=Fi,bn=r.firstChild}let t=bn.cloneNode(!0);e.appendChild(t);let n;t.addEventListener("click",()=>{clearTimeout(n);let r=Array.from(e.querySelectorAll("code > *")).filter(i=>!i.classList.contains("unselectable")||i.textContent!=="$ ").map(i=>i.textContent).join("");navigator.clipboard.writeText(r),t.classList.add("clicked"),t.disabled=!0,n=setTimeout(()=>{t.classList.remove("clicked"),t.disabled=!1},3e3)})})}window.addEventListener("exdoc:loaded",Bl);function Bl(){"clipboard"in navigator&&A("a.copy-markdown").forEach(e=>{e.addEventListener("click",Ql)})}function Ql(e){if(e.ctrlKey||e.metaKey)return;e.preventDefault();let n=e.currentTarget.href,r=new ClipboardItem({"text/plain":fetch(n).then(i=>{if(!i.ok)throw new Error("Failed to fetch markdown");return i.text()}).then(i=>new Blob([i],{type:"text/plain"}))});navigator.clipboard.write([r]).then(()=>{Ve("Page copied as Markdown to clipboard")}).catch(i=>{console.log("Copying Markdown failed:",i),window.confirm("Could not copy to clipboard. Do you want to open the Markdown page instead?")&&(window.location.href=n)})}var F=Y(Ui());var ji=Y(ae());var Wi=ji.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return" Search results for "+e.escapeExpression((s=(s=o(n,"value")||(t!=null?o(t,"value"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"value",hash:{},data:i,loc:{start:{line:3,column:27},end:{line:3,column:36}}}):s))+` -`},3:function(e,t,n,r,i){return` Invalid search -`},5:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"each").call(t??(e.nullContext||{}),t!=null?o(t,"results"):t,{name:"each",hash:{},fn:e.program(6,i,0),inverse:e.noop,data:i,loc:{start:{line:10,column:2},end:{line:21,column:11}}}))!=null?s:""},6:function(e,t,n,r,i){var s,o=e.lambda,a=e.escapeExpression,l=e.lookupProperty||function(u,c){if(Object.prototype.hasOwnProperty.call(u,c))return u[c]};return`
-

- - `+a(o(t!=null?l(t,"title"):t,t))+" ("+a(o(t!=null?l(t,"type"):t,t))+`) - -

-`+((s=l(n,"each").call(t??(e.nullContext||{}),t!=null?l(t,"excerpts"):t,{name:"each",hash:{},fn:e.program(7,i,0),inverse:e.noop,data:i,loc:{start:{line:17,column:8},end:{line:19,column:17}}}))!=null?s:"")+`
-`},7:function(e,t,n,r,i){var s;return'

'+((s=e.lambda(t,t))!=null?s:"")+`

-`},9:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return((s=(o(n,"isArray")||t&&o(t,"isArray")||e.hooks.helperMissing).call(t??(e.nullContext||{}),t!=null?o(t,"results"):t,{name:"isArray",hash:{},fn:e.program(10,i,0),inverse:e.program(12,i,0),data:i,loc:{start:{line:23,column:2},end:{line:29,column:14}}}))!=null?s:"")+` -

The search functionality is full-text based. Here are some tips:

- -
    -
  • Multiple words (such as foo bar) are searched as OR
  • -
  • Use * anywhere (such as fo*) as wildcard
  • -
  • Use + before a word (such as +foo) to make its presence required
  • -
  • Use - before a word (such as -foo) to make its absence required
  • -
  • Use : to search on a particular field (such as field:word). The available fields are title, doc and type
  • -
  • Use WORD^NUMBER (such as foo^2) to boost the given word
  • -
  • Use WORD~NUMBER (such as foo~2) to do a search with edit distance on word
  • -
- -

To quickly go to a module, type, or function, use the autocompletion feature in the sidebar search.

-`},10:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return"

Sorry, we couldn't find anything for "+e.escapeExpression((s=(s=o(n,"value")||(t!=null?o(t,"value"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"value",hash:{},data:i,loc:{start:{line:24,column:48},end:{line:24,column:57}}}):s))+`.

-`},12:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"value"):t,{name:"if",hash:{},fn:e.program(13,i,0),inverse:e.program(15,i,0),data:i,loc:{start:{line:25,column:2},end:{line:29,column:2}}}))!=null?s:""},13:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return"

Invalid search: "+e.escapeExpression((s=(s=o(n,"errorMessage")||(t!=null?o(t,"errorMessage"):t))!=null?s:e.hooks.helperMissing,typeof s=="function"?s.call(t??(e.nullContext||{}),{name:"errorMessage",hash:{},data:i,loc:{start:{line:26,column:23},end:{line:26,column:39}}}):s))+`.

-`},15:function(e,t,n,r,i){return`

Please type something into the search bar to perform a search.

- `},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=t??(e.nullContext||{}),a=e.lookupProperty||function(l,u){if(Object.prototype.hasOwnProperty.call(l,u))return l[u]};return`

-`+((s=a(n,"if").call(o,t!=null?a(t,"value"):t,{name:"if",hash:{},fn:e.program(1,i,0),inverse:e.program(3,i,0),data:i,loc:{start:{line:2,column:2},end:{line:6,column:9}}}))!=null?s:"")+`

- -`+((s=(a(n,"isNonEmptyArray")||t&&a(t,"isNonEmptyArray")||e.hooks.helperMissing).call(o,t!=null?a(t,"results"):t,{name:"isNonEmptyArray",hash:{},fn:e.program(5,i,0),inverse:e.program(9,i,0),data:i,loc:{start:{line:9,column:0},end:{line:44,column:20}}}))!=null?s:"")},useData:!0});var ql="#search";F.default.tokenizer.separator=/\s+/;F.default.QueryLexer.termSeparator=/\s+/;F.default.Pipeline.registerFunction(Ki,"docTokenSplitter");F.default.Pipeline.registerFunction(Yi,"docTrimmer");window.addEventListener("exdoc:loaded",Fl);function Fl(){let e=window.location.pathname;if(e.endsWith("/search.html")||e.endsWith("/search")){let t=er("q");Vl(t)}}async function Vl(e){if(he(e))En({value:e});else{Mi(e);let t=await $l();try{let n=e.replaceAll(/(\B|\\):/g,"\\:"),r=Zl(t.search(n));En({value:e,results:r})}catch(n){En({value:e,errorMessage:n.message})}}}function En({value:e,results:t,errorMessage:n}){let r=f(ql),i=Wi({value:e,results:t,errorMessage:n});r.innerHTML=i}async function $l(){let e=await Ul();if(e)return e;let t=Yl();return jl(t),t}async function Ul(){try{let e=sessionStorage.getItem(Gi());if(e){let t=await zl(e);return F.default.Index.load(t)}else return null}catch(e){return console.error("Failed to load index: ",e),null}}async function jl(e){try{let t=await Wl(e);sessionStorage.setItem(Gi(),t)}catch(t){console.error("Failed to save index: ",t)}}async function Wl(e){let t=new Blob([JSON.stringify(e)],{type:"application/json"}).stream().pipeThrough(new window.CompressionStream("gzip")),r=await(await new Response(t).blob()).arrayBuffer();return Gl(r)}async function zl(e){let t=new Blob([Kl(e)],{type:"application/json"}).stream().pipeThrough(new window.DecompressionStream("gzip")),n=await new Response(t).text();return JSON.parse(n)}function Gl(e){let t="",n=new Uint8Array(e),r=n.byteLength;for(let i=0;i{this.add(e)})})}function Jl(e){e.pipeline.before(F.default.stemmer,Ki)}function Ki(e){let t=[e],n=/\/\d+$/,r=/\:|\./,i=e.toString();if(i.replace(/^[.,;?!]+|[.,;]+$/g,""),i.startsWith("`")&&i.endsWith("`")&&(i=i.slice(1,-1)),n.test(i)){let o=e.toString().replace(n,"");t.push(e.clone().update(()=>o));let a=o.split(r);if(a.length>1){for(let u of a)t.push(e.clone().update(()=>u));let l=e.toString().split(r);t.push(e.clone().update(()=>l[l.length-1]))}i=a[a.length-1]}else i.startsWith("@")?(i=i.substring(1),t.push(e.clone().update(()=>i))):i.startsWith(":")&&(i=i.substring(1),t.push(e.clone().update(()=>i)));let s=i.split(/\_|\-/);if(s.length>1)for(let o of s)t.push(e.clone().update(()=>o));return t}function Xl(e){e.pipeline.before(F.default.stemmer,Yi)}function Yi(e){return e.update(function(t){return t.replace(/^[^@:\w]+/,"").replace(/[^\?\!\w]+$/,"")})}function Zl(e){return e.filter(t=>zi(t.ref)).map(t=>{let n=zi(t.ref),r=t.matchData.metadata;return{...n,metadata:r,excerpts:eu(n,r)}})}function zi(e){return searchData.items.find(t=>t.ref===e)||null}function eu(e,t){let{doc:n}=e,r=Object.keys(t);return[tu(n,r)]}function tu(e,t){let r=e.split(`\r -\r -`).flatMap(a=>a.split(` - -`)),i;r.length>=2?r[0].trim().startsWith("#")?i=r[1]:i=r[0]:r.length===1?i=r[0]:i=e;let s;i.length>200?s=i.slice(0,200)+"...":s=i;let o=He(s);for(let a of t){if(a.trim()==="")continue;let l=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),u=new RegExp(l,"gi");o=o.replace(u,c=>`${c}`)}return o}var Ji='';var nu='button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',M=null,Sn=null,xn=null,kn=!1;function ru(){M||(document.body.insertAdjacentHTML("beforeend",Ji),M=f(".modal"),M.addEventListener("keydown",e=>{e.key==="Escape"&&ue()}),M.querySelector(".modal-close").addEventListener("click",ue),M.addEventListener("click",e=>{e.target===M&&ue()}))}function Xi(e){if(!kn)if(M.contains(e.target))xn=e.target;else{kn=!0;let t=M.querySelectorAll(nu);xn===t[0]?t[t.length-1].focus():t[0].focus(),kn=!1,xn=document.activeElement}}function wt({title:e,body:t}){ru(),Sn=document.activeElement,document.addEventListener("focus",Xi,!0),M.querySelector(".modal-title").innerHTML=e,M.querySelector(".modal-body").innerHTML=t,M.classList.add("shown"),M.focus()}function ue(){M?.classList.remove("shown"),document.removeEventListener("focus",Xi,!0),Sn?.focus(),Sn=null}function Zi(){return!!M?.classList.contains("shown")}var es='
';var iu="https://hexdocs.pm/%%",su="https://www.erlang.org/doc/apps/%%",ou="https://hex.pm/api/packages?search=name:%%*",au=".display-quick-switch",ns="#quick-switch-input",rs="#quick-switch-results",lu=300,uu=9,is=["erts","asn1","common_test","compiler","crypto","debugger","dialyzer","diameter","edoc","eldap","erl_interface","et","eunit","ftp","inets","jinterface","kernel","megaco","mnesia","observer","odbc","os_mon","parsetools","public_key","reltool","runtime_tools","sasl","snmp","ssh","ssl","stdlib","syntax_tools","tftp","tools","wx","xmerl"],cu=["elixir","eex","ex_unit","hex","iex","logger","mix"].concat(is).map(e=>({name:e})),ss=2,D={autocompleteResults:[],selectedIdx:null};C||window.addEventListener("exdoc:loaded",du);function du(){A(au).forEach(e=>{e.addEventListener("click",Ln)})}function hu(e){if(e.key==="Enter"){let t=e.target.value;pu(t),e.preventDefault()}else e.key==="ArrowUp"?(ts(-1),e.preventDefault()):e.key==="ArrowDown"&&(ts(1),e.preventDefault())}function fu(e){let t=e.target.value;if(t.lengthn.json()).then(n=>{Array.isArray(n)&&(D.autocompleteResults=yu(e,n),D.selectedIdx=null,f(ns).value.length>=ss&&vu(D.autocompleteResults))})}function vu(e){f(rs).replaceChildren(...e.map(({name:t},n)=>{let r=E("div",{class:"quick-switch-result","data-index":n},[t]);return r.addEventListener("click",()=>_n(t)),r}))}function yu(e,t){return cu.concat(t).filter(n=>n.name.toLowerCase().includes(e.toLowerCase())).filter(n=>n.releases===void 0||n.releases[0].has_docs===!0).slice(0,uu)}function ts(e){D.selectedIdx=wu(e);let t=f(".quick-switch-result.selected"),n=f(`.quick-switch-result[data-index="${D.selectedIdx}"]`);t&&t.classList.remove("selected"),n&&n.classList.add("selected")}function wu(e){let t=D.autocompleteResults.length;if(D.selectedIdx===null){if(e>=0)return 0;if(e<0)return t-1}return(D.selectedIdx+e+t)%t}var bu="#settings-modal-content",On=[{key:"c",description:"Toggle sidebar",action:We},{key:"n",description:"Cycle themes",action:ar},{key:"s",description:"Focus search bar",displayAs:"/ or s",action:Tn},{key:"/",action:Tn},{key:"k",hasModifier:!0,action:Tn},{key:"v",description:"Open/focus version select",action:ku},{key:"g",description:"Go to package docs",displayAs:"g",action:Ln},{key:"?",displayAs:"?",description:"Bring up this modal",action:Su}],Pn={shortcutBeingPressed:null};C||(document.addEventListener("keydown",Eu),document.addEventListener("keyup",xu));function Eu(e){if(Pn.shortcutBeingPressed||e.target.matches("input, select, textarea"))return;let t=On.find(n=>n.hasModifier?Qe()&&e.metaKey||e.ctrlKey?n.key===e.key:!1:e.ctrlKey||e.metaKey||e.altKey?!1:n.key===e.key);t&&(Pn.shortcutBeingPressed=t,e.preventDefault(),t.action(e))}function xu(e){Pn.shortcutBeingPressed=null}function Tn(e){ue(),Ni()}function ku(){ue(),kr()?hn():_r().then(hn)}function Su(){_u()?ue():An()}function _u(){return Zi()&&f(bu)}var os=Y(ae());var as=os.template({1:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return(s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"description"):t,{name:"if",hash:{},fn:e.program(2,i,0),inverse:e.noop,data:i,loc:{start:{line:40,column:6},end:{line:53,column:13}}}))!=null?s:""},2:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
-
- `+e.escapeExpression(e.lambda(t!=null?o(t,"description"):t,t))+` -
-
-`+((s=o(n,"if").call(t??(e.nullContext||{}),t!=null?o(t,"displayAs"):t,{name:"if",hash:{},fn:e.program(3,i,0),inverse:e.program(5,i,0),data:i,loc:{start:{line:46,column:12},end:{line:50,column:19}}}))!=null?s:"")+`
-
-`},3:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return" "+((s=e.lambda(t!=null?o(t,"displayAs"):t,t))!=null?s:"")+` -`},5:function(e,t,n,r,i){var s=e.lookupProperty||function(o,a){if(Object.prototype.hasOwnProperty.call(o,a))return o[a]};return" "+e.escapeExpression(e.lambda(t!=null?s(t,"key"):t,t))+` -`},compiler:[8,">= 4.3.0"],main:function(e,t,n,r,i){var s,o=e.lookupProperty||function(a,l){if(Object.prototype.hasOwnProperty.call(a,l))return a[l]};return`
-
- - - - -
- -
-`},useData:!0});var Lu=".display-settings",Tu="#settings-modal-content",In="#modal-settings-tab",Cn="#modal-keyboard-shortcuts-tab",us="#settings-content",cs="#keyboard-shortcuts-content",Pu=[{title:"Settings",id:"modal-settings-tab"},{title:"Keyboard shortcuts",id:"modal-keyboard-shortcuts-tab"}];window.addEventListener("exdoc:loaded",Ou);function Ou(){A(Lu).forEach(e=>{e.addEventListener("click",An)})}function ls(){f(Cn).classList.remove("active"),f(In).classList.add("active"),f(us).classList.remove("hidden"),f(cs).classList.add("hidden")}function Au(){f(Cn).classList.add("active"),f(In).classList.remove("active"),f(cs).classList.remove("hidden"),f(us).classList.add("hidden")}function An(){wt({title:Pu.map(({id:s,title:o})=>``).join(""),body:as({shortcuts:On})});let e=f(Tu),t=e.querySelector('[name="theme"]'),n=e.querySelector('[name="tooltips"]'),r=e.querySelector('[name="direct_livebook_url"]'),i=e.querySelector('[name="livebook_url"]');R.getAndSubscribe(s=>{t.value=s.theme||"system",n.checked=s.tooltips,s.livebookUrl===null?(r.checked=!1,i.classList.add("hidden"),i.tabIndex=-1):(r.checked=!0,i.classList.remove("hidden"),i.tabIndex=0,i.value=s.livebookUrl)}),t.addEventListener("change",s=>{R.update({theme:s.target.value})}),n.addEventListener("change",s=>{R.update({tooltips:s.target.checked})}),r.addEventListener("change",s=>{let o=s.target.checked?i.value:null;R.update({livebookUrl:o})}),i.addEventListener("input",s=>{R.update({livebookUrl:s.target.value})}),f(In).addEventListener("click",s=>{ls()}),f(Cn).addEventListener("click",s=>{Au()}),ls()}var Rn=new WeakMap;function Mn(e,t,n,r){if(!e&&!Rn.has(t))return!1;let i=Rn.get(t)??new WeakMap;Rn.set(t,i);let s=i.get(n)??new Set;i.set(n,s);let o=s.has(r);return e?s.add(r):s.delete(r),o&&e}function Iu(e,t){let n=e.target;if(n instanceof Text&&(n=n.parentElement),n instanceof Element&&e.currentTarget instanceof Node){let r=n.closest(t);if(r&&e.currentTarget.contains(r))return r}}function Cu(e,t,n,r={}){let{signal:i,base:s=document}=r;if(i?.aborted)return;let{once:o,...a}=r,l=s instanceof Document?s.documentElement:s,u=!!(typeof r=="object"?r.capture:r),c=p=>{let g=Iu(p,String(e));if(g){let m=Object.assign(p,{delegateTarget:g});n.call(l,m),o&&(l.removeEventListener(t,c,a),Mn(!1,l,n,d))}},d=JSON.stringify({selector:e,type:t,capture:u});Mn(!0,l,n,d)||l.addEventListener(t,c,a),i?.addEventListener("abort",()=>{Mn(!1,l,n,d)})}var bt=Cu;function O(){return O=Object.assign?Object.assign.bind():function(e){for(var t=1;tString(e).toLowerCase().replace(/[\s/_.]+/g,"-").replace(/[^\w-]+/g,"").replace(/--+/g,"-").replace(/^-+|-+$/g,"")||t||"",Oe=({hash:e}={})=>window.location.pathname+window.location.search+(e?window.location.hash:""),Ru=(e,t={})=>{let n=O({url:e=e||Oe({hash:!0}),random:Math.random(),source:"swup"},t);window.history.pushState(n,"",e)},Pe=(e=null,t={})=>{e=e||Oe({hash:!0});let n=O({},window.history.state||{},{url:e,random:Math.random(),source:"swup"},t);window.history.replaceState(n,"",e)},Mu=(e,t,n,r)=>{let i=new AbortController;return r=O({},r,{signal:i.signal}),bt(e,t,n,r),{destroy:()=>i.abort()}},N=class e extends URL{constructor(t,n=document.baseURI){super(t.toString(),n),Object.setPrototypeOf(this,e.prototype)}get url(){return this.pathname+this.search}static fromElement(t){let n=t.getAttribute("href")||t.getAttribute("xlink:href")||"";return new e(n)}static fromUrl(t){return new e(t)}};var ge=class extends Error{constructor(t,n){super(t),this.url=void 0,this.status=void 0,this.aborted=void 0,this.timedOut=void 0,this.name="FetchError",this.url=n.url,this.status=n.status,this.aborted=n.aborted||!1,this.timedOut=n.timedOut||!1}};async function Nu(e,t={}){var n;e=N.fromUrl(e).url;let{visit:r=this.visit}=t,i=O({},this.options.requestHeaders,t.headers),s=(n=t.timeout)!=null?n:this.options.timeout,o=new AbortController,{signal:a}=o;t=O({},t,{headers:i,signal:a});let l,u=!1,c=null;s&&s>0&&(c=setTimeout(()=>{u=!0,o.abort("timeout")},s));try{l=await this.hooks.call("fetch:request",r,{url:e,options:t},(v,{url:w,options:x})=>fetch(w,x)),c&&clearTimeout(c)}catch(v){throw u?(this.hooks.call("fetch:timeout",r,{url:e}),new ge(`Request timed out: ${e}`,{url:e,timedOut:u})):v?.name==="AbortError"||a.aborted?new ge(`Request aborted: ${e}`,{url:e,aborted:!0}):v}let{status:d,url:h}=l,p=await l.text();if(d===500)throw this.hooks.call("fetch:error",r,{status:d,response:l,url:h}),new ge(`Server error: ${h}`,{status:d,url:h});if(!p)throw new ge(`Empty response: ${h}`,{status:d,url:h});let{url:g}=N.fromUrl(h),m={url:g,html:p};return!r.cache.write||t.method&&t.method!=="GET"||e!==g||this.cache.set(m.url,m),m}var Hn=class{constructor(t){this.swup=void 0,this.pages=new Map,this.swup=t}get size(){return this.pages.size}get all(){let t=new Map;return this.pages.forEach((n,r)=>{t.set(r,O({},n))}),t}has(t){return this.pages.has(this.resolve(t))}get(t){let n=this.pages.get(this.resolve(t));return n&&O({},n)}set(t,n){n=O({},n,{url:t=this.resolve(t)}),this.pages.set(t,n),this.swup.hooks.callSync("cache:set",void 0,{page:n})}update(t,n){t=this.resolve(t);let r=O({},this.get(t),n,{url:t});this.pages.set(t,r)}delete(t){this.pages.delete(this.resolve(t))}clear(){this.pages.clear(),this.swup.hooks.callSync("cache:clear",void 0,void 0)}prune(t){this.pages.forEach((n,r)=>{t(r,n)&&this.delete(r)})}resolve(t){let{url:n}=N.fromUrl(t);return this.swup.resolveUrl(n)}},Dn=(e,t=document)=>t.querySelector(e),qn=(e,t=document)=>Array.from(t.querySelectorAll(e)),ms=()=>new Promise(e=>{requestAnimationFrame(()=>{requestAnimationFrame(()=>{e()})})});function gs(e){return!!e&&(typeof e=="object"||typeof e=="function")&&typeof e.then=="function"}function Hu(e,t=[]){return new Promise((n,r)=>{let i=e(...t);gs(i)?i.then(n,r):n(i)})}function ds(e,t){let n=e?.closest(`[${t}]`);return n!=null&&n.hasAttribute(t)?n?.getAttribute(t)||!0:void 0}var Bn=class{constructor(t){this.swup=void 0,this.swupClasses=["to-","is-changing","is-rendering","is-popstate","is-animating","is-leaving"],this.swup=t}get selectors(){let{scope:t}=this.swup.visit.animation;return t==="containers"?this.swup.visit.containers:t==="html"?["html"]:Array.isArray(t)?t:[]}get selector(){return this.selectors.join(",")}get targets(){return this.selector.trim()?qn(this.selector):[]}add(...t){this.targets.forEach(n=>n.classList.add(...t))}remove(...t){this.targets.forEach(n=>n.classList.remove(...t))}clear(){this.targets.forEach(t=>{let n=t.className.split(" ").filter(r=>this.isSwupClass(r));t.classList.remove(...n)})}isSwupClass(t){return this.swupClasses.some(n=>t.startsWith(n))}},kt=class{constructor(t,n){this.id=void 0,this.state=void 0,this.from=void 0,this.to=void 0,this.containers=void 0,this.animation=void 0,this.trigger=void 0,this.cache=void 0,this.history=void 0,this.scroll=void 0,this.meta=void 0;let{to:r,from:i,hash:s,el:o,event:a}=n;this.id=Math.random(),this.state=1,this.from={url:i??t.location.url,hash:t.location.hash},this.to={url:r,hash:s},this.containers=t.options.containers,this.animation={animate:!0,wait:!1,name:void 0,native:t.options.native,scope:t.options.animationScope,selector:t.options.animationSelector},this.trigger={el:o,event:a},this.cache={read:t.options.cache,write:t.options.cache},this.history={action:"push",popstate:!1,direction:void 0},this.scroll={reset:!0,target:void 0},this.meta={}}advance(t){this.state=7}};function Du(e){return new kt(this,e)}var Qn=class{constructor(t){this.swup=void 0,this.registry=new Map,this.hooks=["animation:out:start","animation:out:await","animation:out:end","animation:in:start","animation:in:await","animation:in:end","animation:skip","cache:clear","cache:set","content:replace","content:scroll","enable","disable","fetch:request","fetch:error","fetch:timeout","history:popstate","link:click","link:self","link:anchor","link:newtab","page:load","page:view","scroll:top","scroll:anchor","visit:start","visit:transition","visit:abort","visit:end"],this.swup=t,this.init()}init(){this.hooks.forEach(t=>this.create(t))}create(t){this.registry.has(t)||this.registry.set(t,new Map)}exists(t){return this.registry.has(t)}get(t){let n=this.registry.get(t);if(n)return n;console.error(`Unknown hook '${t}'`)}clear(){this.registry.forEach(t=>t.clear())}on(t,n,r={}){let i=this.get(t);if(!i)return console.warn(`Hook '${t}' not found.`),()=>{};let s=O({},r,{id:i.size+1,hook:t,handler:n});return i.set(n,s),()=>this.off(t,n)}before(t,n,r={}){return this.on(t,n,O({},r,{before:!0}))}replace(t,n,r={}){return this.on(t,n,O({},r,{replace:!0}))}once(t,n,r={}){return this.on(t,n,O({},r,{once:!0}))}off(t,n){let r=this.get(t);r&&n?r.delete(n)||console.warn(`Handler for hook '${t}' not found.`):r&&r.clear()}async call(t,n,r,i){let[s,o,a]=this.parseCallArgs(t,n,r,i),{before:l,handler:u,after:c}=this.getHandlers(t,a);await this.run(l,s,o);let[d]=await this.run(u,s,o,!0);return await this.run(c,s,o),this.dispatchDomEvent(t,s,o),d}callSync(t,n,r,i){let[s,o,a]=this.parseCallArgs(t,n,r,i),{before:l,handler:u,after:c}=this.getHandlers(t,a);this.runSync(l,s,o);let[d]=this.runSync(u,s,o,!0);return this.runSync(c,s,o),this.dispatchDomEvent(t,s,o),d}parseCallArgs(t,n,r,i){return n instanceof kt||typeof n!="object"&&typeof r!="function"?[n,r,i]:[void 0,n,r]}async run(t,n=this.swup.visit,r,i=!1){let s=[];for(let{hook:o,handler:a,defaultHandler:l,once:u}of t)if(n==null||!n.done){u&&this.off(o,a);try{let c=await Hu(a,[n,r,l]);s.push(c)}catch(c){if(i)throw c;console.error(`Error in hook '${o}':`,c)}}return s}runSync(t,n=this.swup.visit,r,i=!1){let s=[];for(let{hook:o,handler:a,defaultHandler:l,once:u}of t)if(n==null||!n.done){u&&this.off(o,a);try{let c=a(n,r,l);s.push(c),gs(c)&&console.warn(`Swup will not await Promises in handler for synchronous hook '${o}'.`)}catch(c){if(i)throw c;console.error(`Error in hook '${o}':`,c)}}return s}getHandlers(t,n){let r=this.get(t);if(!r)return{found:!1,before:[],handler:[],after:[],replaced:!1};let i=Array.from(r.values()),s=this.sortRegistrations,o=i.filter(({before:d,replace:h})=>d&&!h).sort(s),a=i.filter(({replace:d})=>d).filter(d=>!0).sort(s),l=i.filter(({before:d,replace:h})=>!d&&!h).sort(s),u=a.length>0,c=[];if(n&&(c=[{id:0,hook:t,handler:n}],u)){let d=a.length-1,{handler:h,once:p}=a[d],g=m=>{let v=a[m-1];return v?(w,x)=>v.handler(w,x,g(m-1)):n};c=[{id:0,hook:t,once:p,handler:h,defaultHandler:g(d)}]}return{found:!0,before:o,handler:c,after:l,replaced:u}}sortRegistrations(t,n){var r,i;return((r=t.priority)!=null?r:0)-((i=n.priority)!=null?i:0)||t.id-n.id||0}dispatchDomEvent(t,n,r){if(n!=null&&n.done)return;let i={hook:t,args:r,visit:n||this.swup.visit};document.dispatchEvent(new CustomEvent("swup:any",{detail:i,bubbles:!0})),document.dispatchEvent(new CustomEvent(`swup:${t}`,{detail:i,bubbles:!0}))}parseName(t){let[n,...r]=t.split(".");return[n,r.reduce((i,s)=>O({},i,{[s]:!0}),{})]}},Bu=e=>{if(e&&e.charAt(0)==="#"&&(e=e.substring(1)),!e)return null;let t=decodeURIComponent(e),n=document.getElementById(e)||document.getElementById(t)||Dn(`a[name='${CSS.escape(e)}']`)||Dn(`a[name='${CSS.escape(t)}']`);return n||e!=="top"||(n=document.body),n},Et="transition",Nn="animation";async function Qu({selector:e,elements:t}){if(e===!1&&!t)return;let n=[];if(t)n=Array.from(t);else if(e&&(n=qn(e,document.body),!n.length))return void console.warn(`[swup] No elements found matching animationSelector \`${e}\``);let r=n.map(i=>(function(s){let{type:o,timeout:a,propCount:l}=(function(u){let c=window.getComputedStyle(u),d=xt(c,`${Et}Delay`),h=xt(c,`${Et}Duration`),p=hs(d,h),g=xt(c,`${Nn}Delay`),m=xt(c,`${Nn}Duration`),v=hs(g,m),w=Math.max(p,v),x=w>0?p>v?Et:Nn:null;return{type:x,timeout:w,propCount:x?x===Et?h.length:m.length:0}})(s);return!(!o||!a)&&new Promise(u=>{let c=`${o}end`,d=performance.now(),h=0,p=()=>{s.removeEventListener(c,g),u()},g=m=>{m.target===s&&((performance.now()-d)/1e3=l&&p())};setTimeout(()=>{h0?await Promise.all(r):e&&console.warn(`[swup] No CSS animation duration defined on elements matching \`${e}\``)}function xt(e,t){return(e[t]||"").split(", ")}function hs(e,t){for(;e.lengthfs(n)+fs(e[r])))}function fs(e){return 1e3*parseFloat(e)}function qu(e,t={},n={}){if(typeof e!="string")throw new Error("swup.navigate() requires a URL parameter");if(this.shouldIgnoreVisit(e,{el:n.el,event:n.event}))return void window.location.assign(e);let{url:r,hash:i}=N.fromUrl(e),s=this.createVisit(O({},n,{to:r,hash:i}));this.performNavigation(s,t)}async function Fu(e,t={}){if(this.navigating){if(this.visit.state>=6)return e.state=2,void(this.onVisitEnd=()=>this.performNavigation(e,t));await this.hooks.call("visit:abort",this.visit,void 0),delete this.visit.to.document,this.visit.state=8}this.navigating=!0,this.visit=e;let{el:n}=e.trigger;t.referrer=t.referrer||this.location.url,t.animate===!1&&(e.animation.animate=!1),e.animation.animate||this.classes.clear();let r=t.history||ds(n,"data-swup-history");typeof r=="string"&&["push","replace"].includes(r)&&(e.history.action=r);let i=t.animation||ds(n,"data-swup-animation");var s,o;typeof i=="string"&&(e.animation.name=i),e.meta=t.meta||{},typeof t.cache=="object"?(e.cache.read=(s=t.cache.read)!=null?s:e.cache.read,e.cache.write=(o=t.cache.write)!=null?o:e.cache.write):t.cache!==void 0&&(e.cache={read:!!t.cache,write:!!t.cache}),delete t.cache;try{await this.hooks.call("visit:start",e,void 0),e.state=3;let a=this.hooks.call("page:load",e,{options:t},async(u,c)=>{let d;return u.cache.read&&(d=this.cache.get(u.to.url)),c.page=d||await this.fetchPage(u.to.url,c.options),c.cache=!!d,c.page});a.then(({html:u})=>{e.advance(5),e.to.html=u,e.to.document=new DOMParser().parseFromString(u,"text/html")});let l=e.to.url+e.to.hash;if(e.history.popstate||(e.history.action==="replace"||e.to.url===this.location.url?Pe(l):(this.currentHistoryIndex++,Ru(l,{index:this.currentHistoryIndex}))),this.location=N.fromUrl(l),e.history.popstate&&this.classes.add("is-popstate"),e.animation.name&&this.classes.add(`to-${ps(e.animation.name)}`),e.animation.wait&&await a,e.done||(await this.hooks.call("visit:transition",e,void 0,async()=>{if(!e.animation.animate)return await this.hooks.call("animation:skip",void 0),void await this.renderPage(e,await a);e.advance(4),await this.animatePageOut(e),e.animation.native&&document.startViewTransition?await document.startViewTransition(async()=>await this.renderPage(e,await a)).finished:await this.renderPage(e,await a),await this.animatePageIn(e)}),e.done))return;await this.hooks.call("visit:end",e,void 0,()=>this.classes.clear()),e.state=7,this.navigating=!1,this.onVisitEnd&&(this.onVisitEnd(),this.onVisitEnd=void 0)}catch(a){if(!a||a!=null&&a.aborted)return void(e.state=8);e.state=9,console.error(a),this.options.skipPopStateHandling=()=>(window.location.assign(e.to.url+e.to.hash),!0),window.history.back()}finally{delete e.to.document}}var Vu=async function(e){await this.hooks.call("animation:out:start",e,void 0,()=>{this.classes.add("is-changing","is-animating","is-leaving")}),await this.hooks.call("animation:out:await",e,{skip:!1},(t,{skip:n})=>{if(!n)return this.awaitAnimations({selector:t.animation.selector})}),await this.hooks.call("animation:out:end",e,void 0)},$u=function(e){var t;let n=e.to.document;if(!n)return!1;let r=((t=n.querySelector("title"))==null?void 0:t.innerText)||"";document.title=r;let i=qn('[data-swup-persist]:not([data-swup-persist=""])'),s=e.containers.map(o=>{let a=document.querySelector(o),l=n.querySelector(o);return a&&l?(a.replaceWith(l.cloneNode(!0)),!0):(a||console.warn(`[swup] Container missing in current document: ${o}`),l||console.warn(`[swup] Container missing in incoming document: ${o}`),!1)}).filter(Boolean);return i.forEach(o=>{let a=o.getAttribute("data-swup-persist"),l=Dn(`[data-swup-persist="${a}"]`);l&&l!==o&&l.replaceWith(o)}),s.length===e.containers.length},Uu=function(e){let t={behavior:"auto"},{target:n,reset:r}=e.scroll,i=n??e.to.hash,s=!1;return i&&(s=this.hooks.callSync("scroll:anchor",e,{hash:i,options:t},(o,{hash:a,options:l})=>{let u=this.getAnchorElement(a);return u&&u.scrollIntoView(l),!!u})),r&&!s&&(s=this.hooks.callSync("scroll:top",e,{options:t},(o,{options:a})=>(window.scrollTo(O({top:0,left:0},a)),!0))),s},ju=async function(e){if(e.done)return;let t=this.hooks.call("animation:in:await",e,{skip:!1},(n,{skip:r})=>{if(!r)return this.awaitAnimations({selector:n.animation.selector})});await ms(),await this.hooks.call("animation:in:start",e,void 0,()=>{this.classes.remove("is-animating")}),await t,await this.hooks.call("animation:in:end",e,void 0)},Wu=async function(e,t){if(e.done)return;e.advance(6);let{url:n}=t;this.isSameResolvedUrl(Oe(),n)||(Pe(n),this.location=N.fromUrl(n),e.to.url=this.location.url,e.to.hash=this.location.hash),await this.hooks.call("content:replace",e,{page:t},(r,{})=>{if(this.classes.remove("is-leaving"),r.animation.animate&&this.classes.add("is-rendering"),!this.replaceContent(r))throw new Error("[swup] Container mismatch, aborting");r.animation.animate&&(this.classes.add("is-changing","is-animating","is-rendering"),r.animation.name&&this.classes.add(`to-${ps(r.animation.name)}`))}),await this.hooks.call("content:scroll",e,void 0,()=>this.scrollToContent(e)),await this.hooks.call("page:view",e,{url:this.location.url,title:document.title})},zu=function(e){var t;if(t=e,!!t?.isSwupPlugin){if(e.swup=this,!e._checkRequirements||e._checkRequirements())return e._beforeMount&&e._beforeMount(),e.mount(),this.plugins.push(e),this.plugins}else console.error("Not a swup plugin instance",e)};function Gu(e){let t=this.findPlugin(e);if(t)return t.unmount(),t._afterUnmount&&t._afterUnmount(),this.plugins=this.plugins.filter(n=>n!==t),this.plugins;console.error("No such plugin",t)}function Ku(e){return this.plugins.find(t=>t===e||t.name===e||t.name===`Swup${String(e)}`)}function Yu(e){if(typeof this.options.resolveUrl!="function")return console.warn("[swup] options.resolveUrl expects a callback function."),e;let t=this.options.resolveUrl(e);return t&&typeof t=="string"?t.startsWith("//")||t.startsWith("http")?(console.warn("[swup] options.resolveUrl needs to return a relative url"),e):t:(console.warn("[swup] options.resolveUrl needs to return a url"),e)}function Ju(e,t){return this.resolveUrl(e)===this.resolveUrl(t)}var Xu={animateHistoryBrowsing:!1,animationSelector:'[class*="transition-"]',animationScope:"html",cache:!0,containers:["#swup"],hooks:{},ignoreVisit:(e,{el:t}={})=>!(t==null||!t.closest("[data-no-swup]")),linkSelector:"a[href]",linkToSelf:"scroll",native:!1,plugins:[],resolveUrl:e=>e,requestHeaders:{"X-Requested-With":"swup",Accept:"text/html, application/xhtml+xml"},skipPopStateHandling:e=>{var t;return((t=e.state)==null?void 0:t.source)!=="swup"},timeout:0},St=class{get currentPageUrl(){return this.location.url}constructor(t={}){var n,r;this.version="4.8.2",this.options=void 0,this.defaults=Xu,this.plugins=[],this.visit=void 0,this.cache=void 0,this.hooks=void 0,this.classes=void 0,this.location=N.fromUrl(window.location.href),this.currentHistoryIndex=void 0,this.clickDelegate=void 0,this.navigating=!1,this.onVisitEnd=void 0,this.use=zu,this.unuse=Gu,this.findPlugin=Ku,this.log=()=>{},this.navigate=qu,this.performNavigation=Fu,this.createVisit=Du,this.delegateEvent=Mu,this.fetchPage=Nu,this.awaitAnimations=Qu,this.renderPage=Wu,this.replaceContent=$u,this.animatePageIn=ju,this.animatePageOut=Vu,this.scrollToContent=Uu,this.getAnchorElement=Bu,this.getCurrentUrl=Oe,this.resolveUrl=Yu,this.isSameResolvedUrl=Ju,this.options=O({},this.defaults,t),this.handleLinkClick=this.handleLinkClick.bind(this),this.handlePopState=this.handlePopState.bind(this),this.cache=new Hn(this),this.classes=new Bn(this),this.hooks=new Qn(this),this.visit=this.createVisit({to:""}),this.currentHistoryIndex=(n=(r=window.history.state)==null?void 0:r.index)!=null?n:1,this.enable()}async enable(){var t;let{linkSelector:n}=this.options;this.clickDelegate=this.delegateEvent(n,"click",this.handleLinkClick),window.addEventListener("popstate",this.handlePopState),this.options.animateHistoryBrowsing&&(window.history.scrollRestoration="manual"),this.options.native=this.options.native&&!!document.startViewTransition,this.options.plugins.forEach(r=>this.use(r));for(let[r,i]of Object.entries(this.options.hooks)){let[s,o]=this.hooks.parseName(r);this.hooks.on(s,i,o)}((t=window.history.state)==null?void 0:t.source)!=="swup"&&Pe(null,{index:this.currentHistoryIndex}),await ms(),await this.hooks.call("enable",void 0,void 0,()=>{let r=document.documentElement;r.classList.add("swup-enabled"),r.classList.toggle("swup-native",this.options.native)})}async destroy(){this.clickDelegate.destroy(),window.removeEventListener("popstate",this.handlePopState),this.cache.clear(),this.options.plugins.forEach(t=>this.unuse(t)),await this.hooks.call("disable",void 0,void 0,()=>{let t=document.documentElement;t.classList.remove("swup-enabled"),t.classList.remove("swup-native")}),this.hooks.clear()}shouldIgnoreVisit(t,{el:n,event:r}={}){let{origin:i,url:s,hash:o}=N.fromUrl(t);return i!==window.location.origin||!(!n||!this.triggerWillOpenNewWindow(n))||!!this.options.ignoreVisit(s+o,{el:n,event:r})}handleLinkClick(t){let n=t.delegateTarget,{href:r,url:i,hash:s}=N.fromElement(n);if(this.shouldIgnoreVisit(r,{el:n,event:t}))return;if(this.navigating&&i===this.visit.to.url)return void t.preventDefault();let o=this.createVisit({to:i,hash:s,el:n,event:t});t.metaKey||t.ctrlKey||t.shiftKey||t.altKey?this.hooks.callSync("link:newtab",o,{href:r}):t.button===0&&this.hooks.callSync("link:click",o,{el:n,event:t},()=>{var a;let l=(a=o.from.url)!=null?a:"";t.preventDefault(),i&&i!==l?this.isSameResolvedUrl(i,l)||this.performNavigation(o):s?this.hooks.callSync("link:anchor",o,{hash:s},()=>{Pe(i+s),this.scrollToContent(o)}):this.hooks.callSync("link:self",o,void 0,()=>{this.options.linkToSelf==="navigate"?this.performNavigation(o):(Pe(i),this.scrollToContent(o))})})}handlePopState(t){var n,r,i,s;let o=(n=(r=t.state)==null?void 0:r.url)!=null?n:window.location.href;if(this.options.skipPopStateHandling(t)||this.isSameResolvedUrl(Oe(),this.location.url))return;let{url:a,hash:l}=N.fromUrl(o),u=this.createVisit({to:a,hash:l,event:t});u.history.popstate=!0;let c=(i=(s=t.state)==null?void 0:s.index)!=null?i:0;c&&c!==this.currentHistoryIndex&&(u.history.direction=c-this.currentHistoryIndex>0?"forwards":"backwards",this.currentHistoryIndex=c),u.animation.animate=!1,u.scroll.reset=!1,u.scroll.target=!1,this.options.animateHistoryBrowsing&&(u.animation.animate=!0,u.scroll.reset=!0),this.hooks.callSync("history:popstate",u,{event:t},()=>{this.performNavigation(u)})}triggerWillOpenNewWindow(t){return!!t.matches('[download], [target="_blank"]')}};function Ae(){return Ae=Object.assign?Object.assign.bind():function(e){for(var t=1;tString(e).split(".").map(t=>String(parseInt(t||"0",10))).concat(["0","0"]).slice(0,3).join("."),ve=class{constructor(){this.isSwupPlugin=!0,this.swup=void 0,this.version=void 0,this.requires={},this.handlersToUnregister=[]}mount(){}unmount(){this.handlersToUnregister.forEach(t=>t()),this.handlersToUnregister=[]}_beforeMount(){if(!this.name)throw new Error("You must define a name of plugin when creating a class.")}_afterUnmount(){}_checkRequirements(){return typeof this.requires!="object"||Object.entries(this.requires).forEach(([t,n])=>{if(!(function(r,i,s){let o=(function(a,l){var u;if(a==="swup")return(u=l.version)!=null?u:"";{var c;let d=l.findPlugin(a);return(c=d?.version)!=null?c:""}})(r,s);return!!o&&((a,l)=>l.every(u=>{let[,c,d]=u.match(/^([\D]+)?(.*)$/)||[];var h,p;return((g,m)=>{let v={"":w=>w===0,">":w=>w>0,">=":w=>w>=0,"<":w=>w<0,"<=":w=>w<=0};return(v[m]||v[""])(g)})((p=d,h=vs(h=a),p=vs(p),h.localeCompare(p,void 0,{numeric:!0})),c||">=")}))(o,i)})(t,n=Array.isArray(n)?n:[n],this.swup)){let r=`${t} ${n.join(", ")}`;throw new Error(`Plugin version mismatch: ${this.name} requires ${r}`)}}),!0}on(t,n,r={}){var i;n=!(i=n).name.startsWith("bound ")||i.hasOwnProperty("prototype")?n.bind(this):n;let s=this.swup.hooks.on(t,n,r);return this.handlersToUnregister.push(s),s}once(t,n,r={}){return this.on(t,n,Ae({},r,{once:!0}))}before(t,n,r={}){return this.on(t,n,Ae({},r,{before:!0}))}replace(t,n,r={}){return this.on(t,n,Ae({},r,{replace:!0}))}off(t,n){return this.swup.hooks.off(t,n)}};(function(){if(!(typeof window>"u"||typeof document>"u"||typeof HTMLElement>"u")){var e=!1;try{var t=document.createElement("div");t.addEventListener("focus",function(s){s.preventDefault(),s.stopPropagation()},!0),t.focus(Object.defineProperty({},"preventScroll",{get:function(){if(navigator&&typeof navigator.userAgent<"u"&&navigator.userAgent&&navigator.userAgent.match(/Edge\/1[7-8]/))return e=!1;e=!0}}))}catch{}if(HTMLElement.prototype.nativeFocus===void 0&&!e){HTMLElement.prototype.nativeFocus=HTMLElement.prototype.focus;var n=function(s){for(var o=s.parentNode,a=[],l=document.scrollingElement||document.documentElement;o&&o!==l;)(o.offsetHeightn.replace(`{${r}}`,t[r]||""),e||"")}var Vn=class{constructor(){var t;this.id="swup-announcer",this.style="position:absolute;top:0;left:0;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden;white-space:nowrap;word-wrap:normal;width:1px;height:1px;",this.region=void 0,this.region=(t=this.getRegion())!=null?t:this.createRegion()}getRegion(){return document.getElementById(this.id)}createRegion(){let t=(function(n){let r=document.createElement("template");return r.innerHTML=n,r.content.children[0]})(`

`);return document.body.appendChild(t),t}announce(t,n=0){return new Promise(r=>{setTimeout(()=>{this.region.textContent===t&&(t=`${t}.`),this.region.textContent="",this.region.textContent=t,r()},n)})}};function ws(e){let t;if(t=typeof e=="string"?document.querySelector(e):e,!(t instanceof HTMLElement))return;let n=t.getAttribute("tabindex");t.setAttribute("tabindex","-1"),t.focus({preventScroll:!0}),n!==null&&t.setAttribute("tabindex",n)}var _t=class extends ve{constructor(t={}){super(),this.name="SwupA11yPlugin",this.requires={swup:">=4"},this.defaults={headingSelector:"h1",respectReducedMotion:!0,autofocus:!1,announcements:{visit:"Navigated to: {title}",url:"New page at {url}"}},this.options=void 0,this.announcer=void 0,this.announcementDelay=100,this.rootSelector="body",this.handleAnchorScroll=(n,{hash:r})=>{let i=this.swup.getAnchorElement(r);i instanceof HTMLElement&&ws(i)},this.options=Fn({},this.defaults,t),this.announcer=new Vn}mount(){this.swup.hooks.create("content:announce"),this.swup.hooks.create("content:focus"),this.before("visit:start",this.prepareVisit),this.on("visit:start",this.markAsBusy),this.on("visit:end",this.unmarkAsBusy),this.on("visit:end",this.focusContent),this.on("visit:end",this.announceContent),this.on("scroll:anchor",this.handleAnchorScroll),this.before("visit:start",this.disableAnimations),this.before("link:self",this.disableAnimations),this.before("link:anchor",this.disableAnimations),this.swup.announce=this.announce.bind(this)}unmount(){this.swup.announce=void 0}async announce(t){await this.announcer.announce(t)}markAsBusy(){document.documentElement.setAttribute("aria-busy","true")}unmarkAsBusy(){document.documentElement.removeAttribute("aria-busy")}prepareVisit(t){t.a11y={announce:void 0,focus:this.rootSelector}}announceContent(t){this.swup.hooks.callSync("content:announce",t,void 0,n=>{n.a11y.announce===void 0&&(n.a11y.announce=this.getPageAnnouncement()),n.a11y.announce&&this.announcer.announce(n.a11y.announce,this.announcementDelay)})}focusContent(t){this.swup.hooks.callSync("content:focus",t,void 0,n=>{n.a11y.focus&&(this.options.autofocus&&(function(){let r=(function(){let i=document.querySelector("body [autofocus]");if(i&&!i.closest('[inert], [aria-disabled], [aria-hidden="true"]'))return i})();return!!r&&(r!==document.activeElement&&r.focus(),!0)})()===!0||ws(n.a11y.focus))})}getPageAnnouncement(){let{headingSelector:t,announcements:n}=this.options;return(function({headingSelector:r="h1",announcements:i={}}){var s,o;let a=document.documentElement.lang||"*",{href:l,url:u,pathname:c}=N.fromUrl(window.location.href),d=(s=(o=i[a])!=null?o:i["*"])!=null?s:i;if(typeof d!="object")return;let h=document.querySelector(r);h||console.warn(`SwupA11yPlugin: No main heading (${r}) found on new page`);let p=h?.getAttribute("aria-label")||h?.textContent||document.title||ys(d.url,{href:l,url:u,path:c});return ys(d.visit,{title:p,href:l,url:u,path:c})})({headingSelector:t,announcements:n})}disableAnimations(t){this.options.respectReducedMotion&&window.matchMedia("(prefers-reduced-motion: reduce)").matches&&(t.animation.animate=!1,t.scroll.animate=!1)}};function $n(){return $n=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let a=Math.random()*this.trickleValue;this.setValue(this.value+a)},t!==void 0&&(this.className=String(t)),n!==void 0&&(this.styleAttr=String(n)),r!==void 0&&(this.animationDuration=Number(r)),i!==void 0&&(this.minValue=Number(i)),s!==void 0&&(this.initialValue=Number(s)),o!==void 0&&(this.trickleValue=Number(o)),this.styleElement=this.createStyleElement(),this.progressElement=this.createProgressElement()}get defaultStyles(){return` - .${this.className} { - position: fixed; - display: block; - top: 0; - left: 0; - width: 100%; - height: 3px; - background-color: black; - z-index: 9999; - transition: - transform ${this.animationDuration}ms ease-out, - opacity ${this.animationDuration/2}ms ${this.animationDuration/2}ms ease-in; - transform: translate3d(0, 0, 0) scaleX(var(--progress, 0)); - transform-origin: 0; - } - `}show(){this.visible||(this.visible=!0,this.installStyleElement(),this.installProgressElement(),this.startTrickling())}hide(){this.visible&&!this.hiding&&(this.hiding=!0,this.fadeProgressElement(()=>{this.uninstallProgressElement(),this.stopTrickling(),this.visible=!1,this.hiding=!1}))}setValue(t){this.value=Math.min(1,Math.max(this.minValue,t)),this.refresh()}installStyleElement(){document.head.prepend(this.styleElement)}installProgressElement(){this.progressElement.style.setProperty("--progress",String(0)),this.progressElement.style.opacity="1",document.body.prepend(this.progressElement),this.progressElement.scrollTop=0,this.setValue(Math.random()*this.initialValue)}fadeProgressElement(t){this.progressElement.style.opacity="0",setTimeout(t,1.5*this.animationDuration)}uninstallProgressElement(){this.progressElement.remove()}startTrickling(){this.trickleInterval||(this.trickleInterval=window.setInterval(this.trickle,this.animationDuration))}stopTrickling(){window.clearInterval(this.trickleInterval),delete this.trickleInterval}refresh(){requestAnimationFrame(()=>{this.progressElement.style.setProperty("--progress",String(this.value))})}createStyleElement(){let t=document.createElement("style");return this.styleAttr.split(" ").forEach(n=>t.setAttribute(n,"")),t.textContent=this.defaultStyles,t}createProgressElement(){let t=document.createElement("div");return t.className=this.className,t.setAttribute("aria-hidden","true"),t}},Lt=class extends ve{constructor(t={}){super(),this.name="SwupProgressPlugin",this.defaults={className:"swup-progress-bar",delay:300,transition:300,minValue:.1,initialValue:.25,finishAnimation:!0},this.options=void 0,this.progressBar=void 0,this.showProgressBarTimeout=void 0,this.hideProgressBarTimeout=void 0,this.options=$n({},this.defaults,t);let{className:n,minValue:r,initialValue:i,transition:s}=this.options;this.progressBar=new Un({className:n,minValue:r,initialValue:i,animationDuration:s})}mount(){this.on("visit:start",this.startShowingProgress),this.on("page:view",this.stopShowingProgress)}startShowingProgress(){this.progressBar.setValue(0),this.showProgressBarAfterDelay()}stopShowingProgress(){this.progressBar.setValue(1),this.options.finishAnimation?this.finishAnimationAndHideProgressBar():this.hideProgressBar()}showProgressBar(){this.cancelHideProgressBarTimeout(),this.progressBar.show()}showProgressBarAfterDelay(){this.cancelShowProgressBarTimeout(),this.cancelHideProgressBarTimeout(),this.showProgressBarTimeout=window.setTimeout(this.showProgressBar.bind(this),this.options.delay)}hideProgressBar(){this.cancelShowProgressBarTimeout(),this.progressBar.hide()}finishAnimationAndHideProgressBar(){this.cancelShowProgressBarTimeout(),this.hideProgressBarTimeout=window.setTimeout(this.hideProgressBar.bind(this),this.options.transition)}cancelShowProgressBarTimeout(){window.clearTimeout(this.showProgressBarTimeout),delete this.showProgressBarTimeout}cancelHideProgressBarTimeout(){window.clearTimeout(this.hideProgressBarTimeout),delete this.hideProgressBarTimeout}};var bs=()=>{window.dispatchEvent(new Event("exdoc:loaded"))},Zu=(e,{page:t})=>{//i.test(t.html)&&(e.abort(),window.location.reload())};window.addEventListener("DOMContentLoaded",bs);!C&&window.location.protocol!=="file:"&&new St({animationSelector:!1,containers:["#main"],ignoreVisit:e=>{let t=e.split("#")[0];return t===window.location.pathname||t===window.location.pathname+".html"},linkSelector:'a[href]:not([href^="/"]):not([href^="http"])[href$=".html"]',hooks:{"page:load":Zu,"page:view":bs},plugins:[new _t,new Lt({delay:500})]});})(); -/*! Bundled license information: - -lunr/lunr.js: - (** - * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 - * Copyright (C) 2020 Oliver Nightingale - * @license MIT - *) - (*! - * lunr.utils - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Set - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.tokenizer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Pipeline - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Vector - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.stemmer - * Copyright (C) 2020 Oliver Nightingale - * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt - *) - (*! - * lunr.stopWordFilter - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.trimmer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.TokenSet - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Index - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Builder - * Copyright (C) 2020 Oliver Nightingale - *) -*/ diff --git a/doc/dist/html-erlang-KCHZLXSC.css b/doc/dist/html-erlang-KCHZLXSC.css deleted file mode 100644 index 4dd7edb..0000000 --- a/doc/dist/html-erlang-KCHZLXSC.css +++ /dev/null @@ -1,6 +0,0 @@ -:root{--main: hsl(0, 100%, 44%);--mainDark: hsl(0, 100%, 34%);--mainDarkest: hsl(0, 100%, 24%);--mainLight: hsl(0, 100%, 64%);--mainLightest: hsl(0, 100%, 74%);--searchBarFocusColor: hsl(0, 100%, 50%);--searchBarBorderColor: rgb(255, 71, 71, .1);--link-color: hsl(212, 96%, 45%);--link-visited-color: hsl(212, 96%, 40%)}body.dark{--link-color: hsl(212, 56%, 72%);--link-visited-color: hsl(212, 56%, 67%)}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-ext-400-normal-N27NCBWW.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:400;src:url("./lato-latin-400-normal-W7754I4D.woff2") format("woff2"),url("./lato-all-400-normal-MNITWADU.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-ext-700-normal-Q2L5DVMW.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Lato;font-style:normal;font-display:swap;font-weight:700;src:url("./lato-latin-700-normal-2XVSBPG4.woff2") format("woff2"),url("./lato-all-700-normal-XMT5XFBS.woff") format("woff");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--content-width: 949px;--content-gutter: 60px;--borderRadius-lg: 14px;--borderRadius-base: 8px;--borderRadius-sm: 3px;--navTabBorderWidth: 2px;--sansFontFamily: "Lato", system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";--monoFontFamily: ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;--baseLineHeight: 1.5em;--gray10: hsl(207, 43%, 98.5%);--gray25: hsl(207, 43%, 98%);--gray50: hsl(207, 43%, 96%);--gray100: hsl(212, 33%, 91%);--gray200: hsl(210, 29%, 88%);--gray300: hsl(210, 26%, 76%);--gray400: hsl(210, 21%, 64%);--gray450: hsl(210, 21%, 49%);--gray500: hsl(210, 21%, 34%);--gray600: hsl(210, 27%, 26%);--gray650: hsl(211, 32%, 22%);--gray700: hsl(212, 35%, 17%);--gray750: hsl(214, 46%, 14%);--gray800: hsl(216, 52%, 11%);--gray800-opacity-0: hsla(216, 52%, 11%, 0%);--gray850: hsl(216, 63%, 8%);--gray900: hsl(218, 73%, 4%);--gray900-opacity-50: hsla(218, 73%, 4%, 50%);--gray900-opacity-0: hsla(218, 73%, 4%, 0%);--coldGrayFaint: hsl(240, 5%, 97%);--coldGrayLight: hsl(240, 5%, 88%);--coldGray-lightened-10: hsl(240, 5%, 56%);--coldGray: hsl(240, 5%, 46%);--coldGray-opacity-10: hsla(240, 5%, 46%, 10%);--coldGrayDark: hsl(240, 5%, 28%);--coldGrayDim: hsl(240, 5%, 18%);--yellowLight: hsl(43, 100%, 95%);--yellowDark: hsl(44, 100%, 15%);--yellow: hsl(60, 100%, 43%);--green-lightened-10: hsl(90, 100%, 45%);--green: hsl(90, 100%, 35%);--white: hsl(0, 0%, 100%);--white-opacity-80: hsla(0, 0%, 100%, 80%);--white-opacity-50: hsla(0, 0%, 100%, 50%);--white-opacity-10: hsla(0, 0%, 100%, 10%);--white-opacity-0: hsla(0, 0%, 100%, 0%);--black: hsl(0, 0%, 0%);--black-opacity-80: hsla(0, 0%, 0%, 80%);--black-opacity-50: hsla(0, 0%, 0%, 50%);--black-opacity-30: hsla(0, 0%, 0%, 30%);--black-opacity-10: hsla(0, 0%, 0%, 10%);--black-opacity-0: hsla(0, 0%, 0%, 10%);--orangeDark: hsl(30, 90%, 40%);--orangeLight: hsl(30, 80%, 50%);--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--transition-duration: .15s;--transition-timing: cubic-bezier(.4, 0, .2, 1);--transition-all: all var(--transition-duration) var(--transition-timing);--transition-colors: color var(--transition-duration) var(--transition-timing), background-color var(--transition-duration) var(--transition-timing), border-color var(--transition-duration) var(--transition-timing), text-decoration-color var(--transition-duration) var(--transition-timing), fill var(--transition-duration) var(--transition-timing), stroke var(--transition-duration) var(--transition-timing);--transition-opacity: opacity var(--transition-duration) var(--transition-timing)}@media screen and (max-width:768px){:root{--content-width: 100%;--content-gutter: 20px}}option{background-color:var(--sidebarBackground)}:root{--background: var(--white);--contrast: var(--black);--textBody: var(--gray800);--textHeaders: var(--gray900);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--coldGrayFaint);--iconAction: var(--coldGray);--iconActionHover: var(--gray800);--blockquoteBackground: var(--coldGrayFaint);--blockquoteBorder: var(--coldGrayLight);--tableHeadBorder: var(--gray100);--tableBodyBorder: var(--gray50);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray100);--admMixAmt: 25%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 100%, 96%) 100%);--warningHeading: hsl(var(--warningHue), 95%, 38%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 85%, 96%) 100%);--errorHeading: hsl(var(--errorHue), 80%, 40%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 90%, 96%) 100%);--infoHeading: hsl(var(--infoHue), 60%, 30%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 93%) 100%);--neutralHeading: hsl(var(--neutralHue), 35%, 30%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 93%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 25%);--fnSpecAttr: var(--coldGray);--fnDeprecated: var(--yellowLight);--blink: var(--yellowLight);--codeBackground: var(--gray10);--codeBorder: var(--gray100);--codeScrollThumb: var(--gray400);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--white-opacity-80);--admCodeBorder: var(--black-opacity-10);--admCodeColor: var(--black);--tabBorder: var(--gray300);--tabBorderTop: var(--gray100);--tabShadow: var(--gray25);--bottomActionsBtnBorder: var(--black-opacity-10);--bottomActionsBtnTitle: var(--mainDark);--modalBackground: var(--white);--settingsInput: var(--gray500);--settingsInputBackground: var(--white);--settingsInputBorder: var(--gray300);--settingsSectionBorder: var(--gray300);--quickSwitchInput: var(--gray500);--quickSwitchContour: var(--coldGray);--success: var(--green);--progressBarColor: var(--gray400);--sidebarAccentMain: var(--black);--sidebarBackground: var(--gray50);--sidebarHeader: var(--gray100);--sidebarMuted: var(--gray800);--sidebarHover: var(--black);--sidebarStaleVersion: var(--orangeDark);--sidebarSubheadings: var(--gray500);--sidebarItem: var(--black);--sidebarInactiveItemBorder: var(--gray500);--sidebarInactiveItemMarker: var(--gray200);--sidebarLanguageAccentBar: var(--mainDark);--sidebarActiveItem: var(--mainDarkest);--searchBarBorder: var(--gray200);--searchAccentMain: var(--gray600);--searchLanguageAccentBar: var(--main);--searchSearch: var(--white);--autocompleteBorder: rgba(3, 9, 19, .1);--autocompletePreview: var(--gray25);--autocompleteSelected: var(--gray25);--autocompleteHover: var(--gray50);--autocompleteBackground: var(--white);--suggestionBorder: var(--gray200);--autocompleteResults: var(--gray600);--autocompleteResultsBold: var(--gray800);--autocompleteLabelBack: var(--gray100);--autocompleteLabelFont: var(--gray600)}body.dark{--background: var(--gray900);--contrast: var(--white);--textBody: var(--gray200);--textHeaders: var(--gray100);--textDetailAccent: var(--mainLight);--textDetailBackground: var(--gray700);--iconAction: var(--coldGray-lightened-10);--iconActionHover: var(--white);--blockquoteBackground: var(--coldGray-opacity-10);--blockquoteBorder: var(--coldGrayDim);--tableHeadBorder: var(--gray600);--tableBodyBorder: var(--gray700);--warningHue: 34;--errorHue: 7;--infoHue: 206;--neutralHue: 212;--tipHue: 142;--admMixCol: var(--gray900);--admMixAmt: 40%;--warningBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--warningHue), 40%, 14%) 100%);--warningHeading: hsl(var(--warningHue), 100%, 50%);--errorBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--errorHue), 40%, 14%) 100%);--errorHeading: hsl(var(--errorHue), 100%, 52%);--infoBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--infoHue), 60%, 15%) 100%);--infoHeading: hsl(var(--infoHue), 100%, 52%);--neutralBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--neutralHue), 40%, 14%) 100%);--neutralHeading: hsl(var(--neutralHue), 50%, 80%);--tipBackground: color-mix(in xyz, var(--admMixCol) var(--admMixAmt), hsl(var(--tipHue), 40%, 14%) 100%);--tipHeading: hsl(var(--tipHue), 55%, 48%);--fnSpecAttr: var(--gray400);--fnDeprecated: var(--yellowDark);--blink: var(--gray600);--codeBackground: var(--gray800);--codeBorder: var(--gray650);--codeScrollThumb: var(--gray500);--codeScrollBackground: var(--codeBorder);--admCodeBackground: var(--black-opacity-30);--admCodeBorder: var(--white-opacity-10);--admCodeColor: var(--gray100);--tabBorder: var(--gray700);--tabBorderTop: var(--gray700);--tabShadow: var(--black);--bottomActionsBtnBorder: var(--white-opacity-10);--bottomActionsBtnTitle: var(--mainLightest);--modalBackground: var(--gray800);--settingsInput: var(--white);--settingsInputBackground: var(--gray700);--settingsInputBorder: var(--gray700);--settingsSectionBorder: var(--gray700);--quickSwitchInput: var(--gray300);--quickSwitchContour: var(--gray500);--success: var(--green-lightened-10);--progressBarColor: var(--gray300);--sidebarAccentMain: var(--gray50);--sidebarBackground: var(--gray800);--sidebarHeader: var(--gray700);--sidebarMuted: var(--gray300);--sidebarHover: var(--white);--sidebarStaleVersion: var(--orangeLight);--sidebarSubheadings: var(--gray400);--sidebarItem: var(--gray200);--sidebarInactiveItemBorder: var(--gray400);--sidebarInactiveItemMarker: var(--gray600);--sidebarLanguageAccentBar: var(--mainLight);--sidebarActiveItem: var(--mainLightest);--searchBarBorder: var(--gray500);--searchAccentMain: var(--gray300);--searchSearch: var(--gray900);--autocompleteBorder: rgba(28,42,60,.75);--autocompletePreview: var(--gray750);--autocompleteSelected: var(--gray750);--autocompleteHover: var(--gray700);--autocompleteBackground: var(--gray800);--suggestionBorder: var(--gray600);--autocompleteResults: var(--gray200);--autocompleteResultsBold: var(--gray100);--autocompleteLabelBack: var(--gray600);--autocompleteLabelFont: rgba(255, 255, 255, .8)}:root:has(body.dark){color-scheme:dark}*,:before,:after{box-sizing:border-box}html{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";line-height:1.15;-webkit-text-size-adjust:100%;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}@font-face{font-family:remixicon;src:url("./remixicon-ZWU5GQ4X.woff2") format("woff2");font-display:swap}[class^=ri-],[class*=" ri-"],.remix-icon{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}:root{--icon-arrow-up-s: "\ea78";--icon-arrow-down-s: "\ea4e";--icon-arrow-right-s: "\ea6e";--icon-add: "\ea13";--icon-subtract: "\f1af";--icon-error-warning: "\eca1";--icon-external-link-line: "\ecaf";--icon-information: "\ee59";--icon-alert: "\ea21";--icon-double-quotes-l: "\ec51";--icon-link-m: "\eeaf";--icon-close-line: "\eb99";--icon-code-s-slash-line: "\ebad";--icon-menu-line: "\ef3e";--icon-search-2-line: "\f0cd";--icon-settings-3-line: "\f0e6";--icon-printer-line: "\f029";--icon-markdown-line: "\ef1e"}.ri-lg{font-size:1.3333em;line-height:.75em;vertical-align:-.0667em}.ri-settings-3-line:before{content:var(--icon-settings-3-line)}.ri-add-line:before{content:var(--icon-add)}.ri-subtract-line:before{content:var(--icon-subtract)}.ri-arrow-up-s-line:before{content:var(--icon-arrow-up-s)}.ri-arrow-down-s-line:before{content:var(--icon-arrow-down-s)}.ri-arrow-right-s-line:before{content:var(--icon-arrow-right-s)}.ri-external-link-line:before{content:var(--icon-external-link-line)}.ri-search-2-line:before{content:var(--icon-search-2-line)}.ri-menu-line:before{content:var(--icon-menu-line)}.ri-close-line:before{content:var(--icon-close-line)}.ri-link-m:before{content:var(--icon-link-m)}.ri-code-s-slash-line:before{content:var(--icon-code-s-slash-line)}.ri-error-warning-line:before{content:var(--icon-error-warning)}.ri-information-line:before{content:var(--icon-information)}.ri-alert-line:before{content:var(--icon-alert)}.ri-double-quotes-l:before{content:var(--icon-double-quotes-l)}.ri-printer-line:before{content:var(--icon-printer-line)}.ri-markdown-line:before{content:var(--icon-markdown-line)}html,body{box-sizing:border-box;height:100%;width:100%}body{--sidebarWidth: 300px;--sidebarMinWidth: 300px;--sidebarTransitionDuration: .3s;background-color:var(--background);color:var(--textBody);font-size:var(--text-md);line-height:1.6875em;outline:none!important}*,*:before,*:after{box-sizing:inherit}.body-wrapper{display:flex;height:100%}.sidebar{display:none;flex-direction:column;width:var(--sidebarWidth);min-width:var(--sidebarMinWidth);max-width:50vw;height:100%;position:fixed;top:0;left:calc(-1 * var(--sidebarWidth));z-index:100;resize:horizontal}.sidebar-button{padding:20px 12px 18px 19px;position:fixed;z-index:200;top:0;left:0;will-change:transform;transform:translate(0)}.content{left:0;width:100%;height:100%;position:absolute}.content .content-inner{container:content / inline-size;max-width:var(--content-width);min-height:100%;margin:0 auto;padding:0 var(--content-gutter) 10px}.content-inner:focus{outline:none}.sidebar-transition .sidebar,.sidebar-transition .sidebar-button,.sidebar-transition .content{transition:all var(--sidebarTransitionDuration) ease-in-out allow-discrete}.sidebar-open .sidebar,.sidebar-transition .sidebar{display:flex}.sidebar-open .sidebar{left:0}.sidebar-open .sidebar-button{transform:translate(calc(var(--sidebarWidth) - 100%))}.sidebar-open .content{width:calc(100% - var(--sidebarWidth));left:var(--sidebarWidth)}@media screen and (max-width:768px){.sidebar-open .content{left:0;width:100%}.sidebar{max-width:90vw}body:not(.sidebar-open) .sidebar-button{position:absolute}}.swup-progress-bar{height:2px;background-color:var(--progressBarColor)}.sidebar{--sidebarFontSize: 16px;--sidebarLineHeight: 20px;font-family:var(--sansFontFamily);font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);background-color:var(--sidebarBackground);color:var(--sidebarAccentMain);overflow:hidden;.sidebar-tabpanel{scrollbar-width:thin}}.apple-os .sidebar{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.sidebar ul{list-style:none}.sidebar ul li{margin:0;padding:0 10px}.sidebar a{color:var(--sidebarAccentMain);text-decoration:none;transition:var(--transition-colors)}.sidebar a:hover{color:var(--sidebarHover)}.sidebar .external-link{margin-left:2.5px}.sidebar .sidebar-header{background-color:var(--sidebarHeader);width:100%}.sidebar .sidebar-projectInfo{display:flex;justify-content:start;align-items:center;gap:8px;margin:8px 34px 12px 14px}.sidebar .sidebar-projectInfo>div{flex:1}.sidebar .sidebar-projectImage{align-self:flex-end}.sidebar .sidebar-projectImage img{display:block;max-width:48px;max-height:48px}.sidebar .sidebar-projectName{font-weight:700;font-size:var(--text-xl);line-height:24px;color:var(--sidebarAccentMain);margin:0;padding:0;word-wrap:break-word;display:block;width:calc(100% - 12px)}.sidebar .sidebar-projectVersion{display:block;position:relative;margin:0;padding:0;font-size:var(--sidebarFontSize);line-height:var(--sidebarLineHeight);color:var(--sidebarMuted);width:calc(100% - 12px)}.sidebar .sidebar-projectVersion form{display:flex}.sidebar .sidebar-projectVersion select{cursor:pointer;position:relative;margin:0;padding:0 0 0 10px;border:none;-webkit-appearance:none;appearance:none;background-color:transparent;color:var(--sidebarMuted);z-index:2}.sidebar .sidebar-projectVersion option{color:initial}.sidebar .sidebar-projectVersionsCaret{position:absolute;left:0;top:2px;z-index:1;font-size:8px;color:var(--sidebarMuted)}.sidebar .sidebar-projectVersion select::-ms-expand{display:none}.sidebar .sidebar-staleVersion{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--sidebarStaleVersion)}.sidebar .sidebar-staleVersion>a{color:var(--sidebarStaleVersion);font-weight:400}.sidebar .sidebar-staleIcon{font-size:var(--text-lg);line-height:0}.sidebar .sidebar-list-nav{display:flex;margin:0;padding:0;overflow:auto;scrollbar-width:thin}.sidebar .sidebar-list-nav :is(li,li button){text-transform:uppercase;letter-spacing:.02em;font-size:var(--text-sm);color:var(--sidebarSubheadings);white-space:nowrap}.sidebar .sidebar-list-nav li{display:inline-block;padding:0}.sidebar .sidebar-list-nav button{background:none;border:0;border-radius:0;-webkit-appearance:none;text-align:inherit;color:inherit;font-weight:inherit;cursor:pointer;display:inline-block;line-height:27px;padding:4px 14px;transition:var(--transition-all)}.sidebar .sidebar-list-nav button{border-bottom:var(--navTabBorderWidth) solid transparent}.sidebar .sidebar-list-nav button:not([aria-selected]):hover{border-bottom:var(--navTabBorderWidth) solid var(--sidebarInactiveItemBorder);color:var(--sidebarAccentMain);transition:var(--transition-all)}.sidebar .sidebar-list-nav button[aria-selected]{border-bottom:var(--navTabBorderWidth) solid var(--sidebarLanguageAccentBar);color:var(--sidebarAccentMain)}.sidebar .sidebar-tabpanel{flex:1 1 .01%;overflow-y:auto;overscroll-behavior:contain;position:relative;-webkit-overflow-scrolling:touch;padding-top:12px;scroll-padding-top:40px}.sidebar .full-list{margin:0;padding:0 0 20px;position:relative}.sidebar .full-list :is(li,a){display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.sidebar .full-list li{padding:0;line-height:27px}.sidebar .full-list li.group{text-transform:uppercase;font-weight:700;font-size:.8em;margin:1.5em 0 0;line-height:1.8em;color:var(--sidebarSubheadings);padding-left:15px}.sidebar .full-list li.nesting-context{font-weight:700;font-size:.9em;line-height:1.8em;color:var(--sidebarSubheadings);margin-top:10px;padding-left:15px}.sidebar .full-list a{margin-right:30px;padding:3px 0 3px 12px;border-left:var(--navTabBorderWidth) solid transparent;color:var(--sidebarItem)}.sidebar .full-list a[aria-selected]{color:var(--sidebarActiveItem)}.sidebar .full-list button{appearance:none;background-color:transparent;border:0;padding:0;cursor:pointer;color:inherit;width:20px;text-align:center;font-size:calc(1.2 * var(--sidebarFontSize));line-height:var(--sidebarLineHeight);position:absolute;display:block;right:10px;transform:translateY(-100%)}.sidebar .full-list a[aria-selected]+button{color:var(--sidebarActiveItem)}.sidebar .full-list button:after{font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:var(--icon-arrow-down-s)}.sidebar .full-list button[aria-expanded=true]:after{content:var(--icon-arrow-up-s)}.sidebar .full-list ul{display:none;margin:10px 0 10px 10px;padding:0}.sidebar .full-list button[aria-expanded=true]+ul{display:block}.sidebar .full-list>li>a{height:27px;line-height:var(--sidebarLineHeight)}.sidebar .full-list>li>a:hover{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li>a[aria-selected]{border-left-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list>li:last-child{margin-bottom:30px}.sidebar .full-list>li.group:first-child{margin-top:0}.sidebar .full-list>li>ul>li:not(:has(li a[aria-selected=true]))>a[aria-selected=true]:before,.sidebar .full-list>li>ul>li>a:hover:before{content:"\2022";position:absolute;margin-left:-15px;color:var(--sidebarActiveItem)}.sidebar .full-list ul li{line-height:var(--sidebarFontSize);padding:0 8px}.sidebar .full-list ul a{padding-left:15px;height:24px}.sidebar .full-list ul button{font-size:var(--sidebarFontSize)}.sidebar .full-list ul button:after{content:var(--icon-add)}.sidebar .full-list ul button[aria-expanded=true]:after{content:var(--icon-subtract)}.sidebar .full-list ul ul{margin:9px 0 9px 10px}.sidebar .full-list ul ul li{height:20px;color:var(--sidebarAccentMain)}.sidebar .full-list ul ul a{border-left:1px solid var(--sidebarInactiveItemMarker);padding:0 10px;height:20px}.sidebar .full-list ul ul a:hover{border-color:var(--sidebarLanguageAccentBar)}.sidebar .full-list ul ul a[aria-selected]{color:var(--sidebarActiveItem);border-color:var(--sidebarLanguageAccentBar)}.sidebar-button{cursor:pointer;background-color:transparent;border:none;font-size:var(--sidebarFontSize);color:var(--sidebarAccentMain)}.sidebar-button:hover{color:var(--sidebarHover)}body:not(.sidebar-open) .sidebar-button{color:var(--contrast)}@media screen and (max-height:500px){.sidebar{overflow-y:auto}.sidebar .full-list{overflow:visible}}.top-search{background-color:var(--background);top:0;z-index:99;position:relative;width:100%;padding:10px 0}.search-settings{display:flex;column-gap:12px;align-items:center;width:100%;position:relative}.search-bar{border:1px solid var(--searchBarBorder);border-radius:var(--borderRadius-base);height:40px;position:relative;width:100%}.top-search .search-bar .search-input{background-color:var(--searchSearch);border:1px solid transparent;border-radius:var(--borderRadius-base);color:var(--searchAccentMain);position:relative;height:38px;padding:8px 40px 8px 12px;width:100%;transition:var(--transition-all)}.top-search .search-bar .search-input::placeholder{color:var(--searchAccentMain);opacity:.5}.top-search .search-bar .search-input:focus{border:1px solid var(--searchBarFocusColor);border-radius:calc(var(--borderRadius-base) - 1px);position:relative}.top-search .search-bar .search-label{position:relative}.top-search .search-bar .search-input-wrapper{position:relative;display:flex;align-items:center}.top-search .search-settings .engine-selector{position:relative}.top-search .search-settings .engine-selector[data-multiple=false]{display:none}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button{cursor:default}.top-search .search-settings .engine-selector[data-multiple=false] .engine-button .ri-arrow-down-s-line{display:none}.top-search .search-settings .engine-button{display:flex;align-items:center;gap:6px;background-color:var(--autocompleteHover);border:none;color:var(--searchAccentMain);cursor:pointer;font-size:var(--text-sm);padding:8px 12px;border-radius:var(--borderRadius-base);transition:var(--transition-all);height:40px}.top-search .search-settings .engine-button:hover,.top-search .search-settings .engine-button[aria-expanded=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-name{font-size:var(--text-sm);white-space:nowrap}.top-search .search-settings .engine-dropdown{position:absolute;top:calc(100% + 10px);right:0;min-width:200px;max-width:50vh;width:max-content;background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;z-index:200}.top-search .search-settings .engine-dropdown[hidden]{display:none}.top-search .search-settings .engine-option{display:block;width:100%;text-align:left;background-color:transparent;border:0;color:var(--textHeaders);cursor:pointer;padding:10px 12px;transition:var(--transition-colors)}.top-search .search-settings .engine-option:hover{background-color:var(--autocompleteHover)}.top-search .search-settings .engine-option:focus{border-left:2px solid var(--main);padding-left:10px;background-color:var(--autocompleteHover);outline:none}.top-search .search-settings .engine-option[aria-checked=true]{background-color:var(--autocompleteSelected)}.top-search .search-settings .engine-option .name{display:block;font-size:var(--text-sm);font-weight:500;margin-bottom:2px}.top-search .search-settings .engine-option .help{display:block;font-size:var(--text-xs);opacity:.7}.top-search .search-bar .search-close-button{font-size:var(--text-md);color:var(--searchAccentMain);background-color:transparent;border:none;cursor:pointer;margin:0;opacity:.5;padding:5px;transform:scaleY(0);transition:var(--transition-all);display:flex;align-items:center;position:absolute;right:0;top:50%;transform:translateY(-50%) scaleY(0)}.top-search .search-bar .search-close-button:hover{opacity:.7}.top-search .search-settings button.icon-settings{display:flex;align-items:center;justify-content:flex-end}.top-search .search-settings .icon-settings{font-size:var(--text-xl);float:right;color:var(--iconAction);text-decoration:none;border:none;transition:color .3s ease-in-out;background-color:transparent;cursor:pointer;padding:0}.top-search .search-settings .icon-settings:hover{color:var(--iconActionHover)}.top-search .search-settings .icon-settings:visited{color:var(--iconAction)}@media screen and (max-width:768px){.top-search{padding-left:calc(var(--content-gutter) + 36px);padding-right:var(--content-gutter);margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));width:calc(2 * var(--content-gutter) + 100%)}.search-settings{width:100%;box-sizing:border-box}.top-search .search-settings .engine-name{display:none}}body.search-focused .search-bar .search-close-button{transform:translateY(-50%) scaleY(1);transition:var(--transition-all)}@media screen and (hover:hover){body.search-focused .top-search{position:sticky!important}body.search-focused .sidebar-button{position:fixed!important}}@media screen and (hover:none){body.scroll-sticky .top-search{position:sticky!important}body.scroll-sticky .sidebar-button{position:fixed!important}}*:focus,button:focus,[type=button]:focus,[type=reset]:focus,[type=submit]:focus{outline:2px solid var(--main);outline-offset:-2px}*:focus:not(:focus-visible),button:focus:not(:focus-visible),[type=button]:focus:not(:focus-visible),[type=reset]:focus:not(:focus-visible),[type=submit]:focus:not(:focus-visible){outline:0}input[type=text],input[type=number],input[type=date],input[type=datetime],input[type=datetime-local],input[type=email],input[type=month],input[type=password],input[type=search],input[type=tel],input[type=time],input[type=url],input[type=week],textarea{outline:0}.content-inner{font-size:1em;line-height:1.6875em;position:relative;background-color:var(--background);color:var(--textBody)}.content-inner .heading-with-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px}.content-inner .heading-with-actions>*:not(h1){flex-shrink:0}.content-inner .heading-with-actions h1{flex-grow:1;justify-self:flex-start;max-width:100%;margin:0;overflow-wrap:break-word}.content-inner .heading-with-actions .icon-action{width:20px;height:20px;display:flex;justify-content:center;align-items:center;font-weight:400;margin-left:3px}.content-inner .heading-with-actions.top-heading .icon-action{font-size:1.2rem}@container content (width > 600px){.content-inner .heading-with-actions.top-heading{flex-wrap:nowrap;align-items:flex-start;& h1{padding-right:32px}.icon-action{padding-top:1.7rem}}}.content-inner .top-heading{padding-top:1rem}.content-inner :is(h1,h2,h3,h4,h5,h6){font-family:var(--sansFontFamily);font-weight:700;line-height:1.5em;word-wrap:break-word;color:var(--textHeaders)}.content-inner div.group-description{margin:0 0 3em}.content-inner h1 small{font-weight:400}.content-inner{& h1{font-size:1.5rem;margin-top:1.75em}& h2{font-size:1.35rem;margin-top:1.5em;margin-bottom:.5em}& h3{font-size:1.25rem;margin-top:1.5em;margin-bottom:.5em}:is(h4,h5,h6){font-size:1.15rem;margin-top:1.25em;margin-bottom:.5em}:is(h5,h6){font-weight:400}& h6{font-size:1rem}}@container content (width > 600px){.content-inner{& h1{font-size:2rem}& h2{font-size:1.75rem}& h3{font-size:1.45rem}}}.content-inner li+li{margin-top:.25em}.content-inner :is(a,.a-main){color:var(--link-color);text-decoration:underline;text-decoration-skip-ink:auto}.content-inner :is(a:visited,.a-main:visited){color:var(--link-visited-color)}.content-inner .icon-action{color:var(--iconAction);text-decoration:none;border:none;transition:var(--transition-colors);background-color:transparent;cursor:pointer}.content-inner .icon-action:hover{color:var(--iconActionHover)}.content-inner .icon-action:visited{color:var(--iconAction)}.content-inner .livebook-badge-container{display:flex}.content-inner a.livebook-badge{display:inline-flex}.content-inner .note{color:var(--iconAction);font-size:var(--text-xs);font-weight:400}.content-inner{:is(blockquote,section.admonition){position:relative;margin:1.5625em 0;padding:0 1.2rem;overflow:auto;border-radius:var(--borderRadius-base);:is(h1,h2,h3,h4,h5,h6){margin:1.25rem 0}:is(h1,h2,h3){font-size:1.375rem}:is(h4,h5,h6){font-size:1rem}:is(code,pre code){background-color:var(--admCodeBackground);border:1px solid var(--admCodeBorder);color:var(--admCodeColor)}:is(a,a:visited){color:inherit}:is(h1,h2,h3,h4,h5,h6):first-child{padding-bottom:1rem;border-bottom:2px solid hsl(from currentColor h s l / 15%)}& p:last-child{padding-bottom:1em;margin-bottom:0}}& blockquote{border-left:3px solid var(--blockquoteBorder);background-color:var(--blockquoteBackground)}}@media print{.content-inner :is(blockquote,section.admonition){border:2px solid hsl(from var(--textBody) h s l / 15%);:is(code,pre code){border-color:var(--gray400)}& pre code.makeup{white-space:break-spaces;break-inside:avoid}}}.content-inner table{margin:2em 0;border-collapse:collapse;display:block;overflow:auto}.content-inner th{text-align:left;font-family:var(--sansFontFamily);font-weight:700;padding-bottom:.5em;white-space:nowrap}.content-inner thead tr{border-bottom:1px solid var(--tableHeadBorder)}.content-inner tbody tr{border-bottom:1px solid var(--tableBodyBorder)}.content-inner tbody tr:last-child{border-bottom:none}.content-inner tr{vertical-align:bottom;height:2.5em}.content-inner :is(td,th){padding:.25em .25em .25em 1em;line-height:2em;vertical-align:top}.content-inner .section-heading{--icon-size: 16px;--icon-spacing: 5px;display:grid;grid-template:1fr / 1fr}@media screen and (max-width:768px){.content-inner .section-heading{--icon-spacing: 2px}}.content-inner .section-heading>:is(.hover-link,.text){grid-row:1;grid-column:1}.content-inner .section-heading .hover-link{text-decoration:none}.content-inner .section-heading i{font-size:var(--icon-size);color:var(--mainLight);top:-2px;margin-left:calc(-1 * (var(--icon-size) + var(--icon-spacing)));padding-right:var(--icon-spacing);position:relative;opacity:0}.content-inner :is(blockquote,section.admonition) .section-heading i{display:none}.content-inner .section-heading:is(:hover,:focus,:target) i{opacity:1}.content-inner .app-vsn{display:none!important;font-size:.6em;line-height:1.5em}@media screen and (max-width:768px){.content-inner .app-vsn{display:block!important}}.content-inner img{max-width:100%}@media screen and (max-width:768px){.content-inner :is(ol,ul){padding-left:calc(1.5 * var(--content-gutter))}}.content-inner section.admonition{border-radius:var(--borderRadius-base)}.content-inner section.admonition>.admonition-title{color:var(--contrast);padding-left:2.2rem;font-weight:700;font-style:normal}.content-inner section.admonition>.admonition-title:before{color:var(--contrast);position:absolute;left:1rem;font-size:1.8rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.content-inner section.admonition{&.warning{background-color:var(--warningBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--warningHeading)}}}&.error{background-color:var(--errorBackground);.admonition-title{&,&:before{content:var(--icon-error-warning);color:var(--errorHeading)}}}&.info{background-color:var(--infoBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--infoHeading)}}}&.neutral{background-color:var(--neutralBackground);.admonition-title{&,&:before{content:var(--icon-double-quotes-l);color:var(--neutralHeading)}}}&.tip{background-color:var(--tipBackground);.admonition-title{&,&:before{content:var(--icon-information);color:var(--tipHeading)}}}}@media print{.content-inner section.admonition{&.warning{border-color:hsl(from var(--warningHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--warningHue),90%,40%)}}}&.error{border-color:hsl(from var(--errorHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--errorHue),80%,40%)}}}&.info{border-color:hsl(from var(--infoHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--infoHue),80%,40%)}}}&.neutral{border-color:hsl(from var(--neutralHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--neutralHue),30%,40%)}}}&.tip{border-color:hsl(from var(--tipHeading) h s l / 15%);.admonition-title{&,&:before{color:hsl(var(--tipHue),50%,40%)}}}}}.content-inner section.admonition>.admonition-title code{margin:0 .5ch}@media screen and (max-width:768px){.content-inner section.admonition{margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter));padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0}}.content-inner .summary h3 a{text-decoration:none;border:none;color:var(--textHeaders)!important}.content-inner .summary span.deprecated{color:var(--darkDeprecated);font-weight:400}.content-inner .summary .summary-row .summary-signature{font-family:var(--monoFontFamily);font-size:13px;font-weight:700}.content-inner .summary .summary-row .summary-signature a{text-decoration:none;border:none}.content-inner .summary .summary-row .summary-synopsis{padding:0 1.2em;margin:0 0 .5em}.content-inner .summary .summary-row .summary-synopsis p{margin:0;padding:0}@font-face{font-family:Consolas;src:local("Consolas");size-adjust:110%}.content-inner.content-inner :is(a:has(code,img),pre a){color:var(--link-color);text-shadow:none;text-decoration:none;background-image:none}.content-inner.content-inner :is(a:has(code,img),pre a):is(:visited,:active,:focus,:hover){color:var(--link-visited-color)}.content-inner strong>code{font-weight:700}.content-inner code{font-family:var(--monoFontFamily);font-style:normal;line-height:24px;font-weight:400;font-size:var(--text-sm);background-color:var(--codeBackground);vertical-align:baseline;border-radius:var(--borderRadius-sm);padding:.1em .2em;border:1px solid var(--codeBorder);text-transform:none}.content-inner code.inline{border-radius:var(--borderRadius-sm);word-wrap:break-word}.content-inner :is(h1,h2,h3,h4,h5,h6) code{font-size:.875em}.content-inner pre{margin:var(--baseLineHeight) 0}.content-inner pre code{display:block;overflow-x:auto;white-space:inherit;padding:1em;scrollbar-width:thin}.content-inner pre code.output{margin:0 12px;max-height:400px;overflow:auto}.content-inner pre code.output+.copy-button{margin-right:12px}.content-inner pre code.output:before{content:"Output";display:block;position:absolute;top:-16px;left:12px;padding:2px 4px;font-size:var(--text-xs);font-family:var(--monoFontFamily);line-height:1;color:var(--textHeaders);background-color:var(--codeBackground);border:1px solid var(--codeBorder);border-bottom:0;border-radius:2px}@media screen and (max-width:768px){.content-inner>pre:has(code),.content-inner section>pre:has(code){margin-left:calc(-1 * var(--content-gutter));margin-right:calc(-1 * var(--content-gutter))}.content-inner>pre code,.content-inner section>pre code{padding-left:var(--content-gutter);padding-right:var(--content-gutter);border-radius:0;border-left-width:0;border-right-width:0}}@keyframes blink-background{0%,to{background-color:var(--textDetailBackground)}50%{background-color:var(--blink)}}.content-inner .detail:target .detail-header{animation-duration:.55s;animation-name:blink-background;animation-iteration-count:1;animation-timing-function:ease-in-out}.content-inner .detail-header{margin:1em 0;padding:.5em .85em .5em 1em;background-color:var(--textDetailBackground);border-left:3px solid var(--textDetailAccent);font-size:1em;font-family:var(--monoFontFamily);position:relative}.content-inner .detail-header .signature{font-family:var(--monoFontFamily);font-weight:700;font-size:13px!important;line-height:2em!important;margin:0!important}.content-inner .detail-header:hover a.detail-link,.content-inner .detail-header a.detail-link:focus{opacity:1;text-decoration:none}.content-inner .detail-header a.detail-link{transition:var(--transition-opacity);position:absolute;top:0;left:0;display:block;opacity:0;padding:.6em;line-height:1.5em;margin-left:-2.5em;text-decoration:none;border:none}@media screen and (max-width:768px){.content-inner .detail-header a.detail-link{margin-left:-30px}}.content-inner .specs pre{font-family:var(--monoFontFamily);font-size:var(--text-xs);font-style:normal;line-height:24px;white-space:pre-wrap;margin:0;padding:0}.content-inner .specs .attribute{color:var(--fnSpecAttr)}.content-inner .docstring{margin:1.2em 0 3em 1.2em}@media screen and (max-width:768px){.content-inner .docstring{margin-left:0}}.content-inner .docstring{:not(.admonition-title){&:is(h1,h2,h3,h4,h5,h6){font-weight:700;font-size:1em;margin-top:2em}&:is(h1,h2){font-size:1.25em}&:is(h3){font-size:1.1em}}}.content-inner div.deprecated{display:block;padding:1em;background-color:var(--fnDeprecated);border-radius:var(--borderRadius-sm);margin:var(--baseLineHeight) 0}.content-inner .footer{margin:4em auto 1em;text-align:center;font-size:var(--text-sm)}.content-inner .footer .line{display:inline-block}.content-inner .footer .line a,.content-inner .footer .line button{padding:0 4px}.content-inner .footer button{background-color:transparent;border:0;cursor:pointer}.content-inner .bottom-actions{display:flex;justify-content:space-between;margin-top:7em;gap:12px}.bottom-actions-item{flex:1 1 0%}.content-inner .bottom-actions .bottom-actions-button{display:flex;text-decoration:none;flex-direction:column;border-radius:var(--borderRadius-sm);border:1px solid var(--bottomActionsBtnBorder);padding:12px 16px;min-width:150px;transition:var(--transition-all)}.content-inner .bottom-actions .bottom-actions-button:hover{border-color:var(--mainLight)}.content-inner .bottom-actions .bottom-actions-button .subheader{font-size:.8em;color:var(--textHeaders);white-space:nowrap}.content-inner .bottom-actions .bottom-actions-button .title{color:var(--bottomActionsBtnTitle)}.content-inner .bottom-actions .bottom-actions-button[rel=prev]{text-align:start}.content-inner .bottom-actions .bottom-actions-button[rel=next]{text-align:end}@media screen and (max-width:768px){.content-inner .bottom-actions{flex-direction:column-reverse}}.page-cheatmd .content-inner{--horizontal-space: 1.5em;--vertical-space: 1em}@media(max-width:600px){.page-cheatmd .content-inner{--horizontal-space: 1em;--vertical-space: .75em}}.page-cheatmd .content-inner{max-width:1200px}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& h1{margin-bottom:var(--vertical-space)}& h2{font-size:1.6rem;margin:var(--vertical-space) 0;column-span:all;color:var(--gray700);font-weight:500}& h3{font-size:1.375rem;margin:0 0 1em;font-weight:400}& section.h3{min-width:300px;margin:0;padding:0 0 calc(var(--vertical-space) * 2) 0;break-inside:avoid}& h3 .text{overflow:hidden}& h3 .text:after{content:"";margin-left:calc(var(--horizontal-space) / 2);vertical-align:baseline;display:inline-block;width:100%;height:1px;margin-right:-100%;margin-bottom:5px;background-color:var(--codeBorder)}& h4{font-size:1rem;display:block;margin:0;padding:.25em var(--horizontal-space);font-weight:400;background:var(--gray100);color:#567;border:solid 1px 1px 0 1px var(--gray100)}.h2 p{margin:0;display:block;background:var(--gray50);padding:var(--vertical-space) var(--horizontal-space)}.h2 p>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}& pre code{padding:var(--vertical-space) var(--horizontal-space)}.h2 pre{margin:0}.h2 pre+pre{margin-top:-1px}& pre.wrap{white-space:break-spaces}@media screen and (max-width:768px){& pre code{border-left-width:1px!important;border-right-width:1px!important}}.h2 table{display:table;box-sizing:border-box;width:100%;border-collapse:collapse;margin:0}.h2 th{padding:var(--vertical-space) var(--horizontal-space);line-height:inherit;margin-bottom:-1px;vertical-align:middle;border-bottom:1px solid var(--codeBorder)}.h2 td{padding:var(--vertical-space) var(--horizontal-space);border:0;border-bottom:1px solid var(--codeBorder)}.h2 tr:first-child{border-top:1px solid var(--codeBorder)}.h2 td code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}.h2 thead{background-color:var(--gray50)}.h2 tbody{background-color:var(--codeBackground)}.h2 :is(ul,ol){margin:0;padding:0}.h2 li{list-style-position:inside;padding:.5em var(--horizontal-space);line-height:2em;vertical-align:middle;background-color:var(--codeBackground);border-bottom:1px solid var(--codeBorder);margin-top:0}.h2 :is(ul,ol)+pre code{border-top:0}.h2 li>code{color:#eb5757;border-radius:var(--borderRadius-sm);padding:.2em .4em}}}@scope(.dark .page-cheatmd)to (.admonition){:scope .content-inner{& h2{color:var(--gray200)}& h4{background:#192f50;color:var(--textBody);border:1px solid #192f50;border-bottom:0}.h2 p{background:var(--gray700)}.h2 thead{background-color:var(--gray700)}}}.page-cheatmd .content-inner{& section.width-50{display:block;width:50%;margin:0;>section>table{width:100%}}& section:is(.col-2,.col-2-left,.col-3){column-gap:40px}& section.col-2{column-count:2;height:auto}& section.col-2-left{display:grid;grid-template-columns:calc(100% / 3) auto;>h2{grid-column-end:span 2}}& section.col-3{column-count:3;height:auto}& section.list-4>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 25%}}& section.list-6>ul{display:flex;flex-wrap:wrap;>li{flex:0 0 calc(100% / 6)}}@media screen and (max-width:1400px){& section.col-3{column-count:2}& section.col-2-left{display:flex;flex-direction:column}}@media screen and (max-width:1200px){& section:is(.col-2,.col-3){display:flex;flex-direction:column}& section.list-6>ul>li{flex:0 0 25%}}@media screen and (max-width:1000px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 calc(100% / 3)}}@media screen and (max-width:600px){& section:is(.list-4,.list-6)>ul>li{flex:0 0 50%}& section.width-50{width:100%}}}@keyframes loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.page-search #search{min-height:200px;position:relative;.loading{height:64px;width:64px;position:absolute;top:50%;left:calc(50% - 32px)}.loading div{box-sizing:border-box;display:block;position:absolute;width:51px;height:51px;margin:6px;border:6px solid var(--coldGray);border-radius:50%;animation:loading 1.2s cubic-bezier(.5,0,.5,1) infinite;border-color:var(--coldGray) transparent transparent transparent}.loading div:nth-child(1){animation-delay:-.45s}.loading div:nth-child(2){animation-delay:-.3s}.loading div:nth-child(3){animation-delay:-.15s}.result{margin:2em 0}.result p{margin:0}.result-id{font-size:1.4em;margin:0}.result-id a{text-decoration:none;color:var(--textHeaders);transition:var(--transition-colors)}.result-id a:is(:visited,:active){color:var(--textHeaders)}.result-id a:is(:hover,:focus){color:var(--main)}:is(.result-id,.result-elem) em{font-style:normal;color:var(--main)}.result-id small{font-weight:400}}@keyframes keyboard-shortcuts-show{0%{opacity:0}to{opacity:1}}.modal{animation-duration:.15s;animation-name:keyboard-shortcuts-show;animation-iteration-count:1;animation-timing-function:ease-in-out;display:none;background-color:#000000bf;position:fixed;inset:0;z-index:300}.modal.shown{display:block}.modal .modal-contents{margin:75px auto 0;max-width:500px;background-color:var(--modalBackground);border-radius:var(--borderRadius-sm);box-shadow:2px 2px 8px #0003;padding:25px 35px 35px}@media screen and (max-width:768px){.modal .modal-contents{padding:20px}}.modal .modal-header{display:flex;align-items:start}.modal .modal-title{display:inline-block;flex-grow:1;font-size:1.2rem;font-weight:700;margin-bottom:20px}.modal .modal-title button{border:none;background-color:transparent;color:var(--textHeaders);font-weight:700;margin-right:30px;padding-left:0;text-align:left;transition:var(--transition-colors)}.modal .modal-title button:hover{color:var(--main);cursor:pointer}.modal .modal-title button.active{color:var(--main)}.modal .modal-close{cursor:pointer;display:block;font-size:1.5rem;margin:-8px -8px 0 0;padding:8px;opacity:.7;background-color:transparent;color:var(--textHeaders);border:none;transition:var(--transition-opacity)}.modal .modal-close:hover{opacity:1}#keyboard-shortcuts-content dl.shortcut-row{display:flex;align-items:center;justify-content:space-between;margin:0;padding:6px 0 8px;border-bottom:1px solid var(--settingsSectionBorder)}#keyboard-shortcuts-content dl.shortcut-row:last-of-type{border-bottom-style:none}#keyboard-shortcuts-content dl.shortcut-row:first-child{padding-top:0}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){display:inline-block}#keyboard-shortcuts-content kbd>kbd{background-color:var(--settingsInputBorder);color:var(--contrast);border-radius:var(--borderRadius-sm);font-family:inherit;font-weight:700;display:inline-block;line-height:1;padding:4px 7px 6px;min-width:26px;text-align:center;font-size:var(--text-sm)}#keyboard-shortcuts-content :is(.shortcut-keys,.shortcut-description){margin:0}#quick-switch-modal-body{width:100%;position:relative}#quick-switch-modal-body .ri-search-2-line{position:absolute;left:0;top:0;padding:4px 10px;color:var(--quickSwitchContour);font-weight:700}#quick-switch-modal-body #quick-switch-input{width:100%;padding:8px 6px 8px 38px;border:none;color:var(--quickSwitchInput);background-color:transparent;border-bottom:1px solid var(--quickSwitchContour);box-sizing:border-box;transition:all .12s ease-out}#quick-switch-modal-body #quick-switch-results{margin:0}#quick-switch-modal-body .quick-switch-result{padding:2px 5px;border-bottom:1px dotted var(--quickSwitchContour);transition:all .12s ease-out}#quick-switch-modal-body .quick-switch-result:last-child{border-bottom:none}#quick-switch-modal-body .quick-switch-result:hover{cursor:pointer}#quick-switch-modal-body .quick-switch-result:is(:hover,.selected){border-left:4px solid var(--main);background-color:var(--codeBackground)}.autocomplete{display:none;position:absolute;width:calc(100% - 32px);top:55px}.autocomplete .triangle{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-bottom:12px solid var(--autocompleteBackground);position:absolute;top:8px;left:26px;transform:translate(-50%);z-index:100;background-color:transparent}.autocomplete-results{margin:0;padding:15px 20px;display:flex;flex-wrap:wrap;justify-content:space-between;gap:8px;color:var(--autocompleteResults);font-weight:300;font-size:.9rem;font-style:italic}.autocomplete-results .query{margin-right:auto}.autocomplete-results .bold{color:var(--autocompleteResultsBold);font-weight:400}.autocomplete.shown{display:block}.autocomplete-container{position:absolute;top:5px;width:100%;z-index:200}.autocomplete-suggestions{background-color:var(--autocompleteBackground);border-radius:var(--borderRadius-base);box-shadow:0 4px 12px #0003;overflow-y:auto;max-height:450px;white-space:normal;overflow-x:hidden;overscroll-behavior-y:contain;scrollbar-width:thin}.autocomplete-suggestion{color:var(--textHeaders)!important;display:block;padding:12px 20px;text-decoration:none!important;transition:var(--transition-colors);border-top:1px solid var(--suggestionBorder);font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.autocomplete-suggestion.selected{background-color:var(--autocompleteSelected)}.autocomplete-suggestion:hover{background-color:var(--autocompleteHover)}.autocomplete-suggestion em{font-style:normal;font-weight:700}.autocomplete-suggestion .separator{opacity:.4;margin:0 2px}.autocomplete-suggestion .description{opacity:.6}.autocomplete-suggestion .label{background-color:var(--autocompleteLabelBack);opacity:.6;color:var(--autocompleteLabelFont);padding:4px 8px;border-radius:4px;margin-left:5px;text-transform:uppercase;font-family:var(--sansFontFamily);font-size:.7rem}@media screen and (hover:none){.autocomplete-results .press-return{display:none!important}}.tooltip{box-shadow:0 0 10px var(--black-opacity-10);max-height:300px;max-width:500px;padding:0;position:absolute;pointer-events:none;margin:0;z-index:99;top:0;left:0;visibility:hidden;transform:translateY(20px);opacity:0;transition:.2s visibility ease-out,.2s transform ease-out,.2s opacity ease-out}.tooltip.tooltip-shown{visibility:visible;transform:translateY(0);opacity:1}.tooltip .tooltip-body{border:1px solid var(--codeBorder);border-radius:var(--borderRadius-sm);overflow:auto}.tooltip .tooltip-body .signature{min-width:320px;width:100%;line-height:1em!important;margin:.75em 0!important}.tooltip .tooltip-body .detail-header{border-left:0;margin-bottom:0;margin-top:0}.tooltip .tooltip-body .docstring{background-color:var(--background);padding:1.2em;margin:0;width:498px}.tooltip .tooltip-body .docstring-plain{max-width:498px;width:auto}.tooltip .tooltip-body .version-info{float:right;font-family:var(--monoFontFamily);font-weight:400;opacity:.3;padding-left:.3em}pre{position:relative}pre:hover .copy-button,pre .copy-button:focus{opacity:1}.copy-button{display:flex;opacity:0;position:absolute;top:7px;right:8px;padding:8px;background-color:transparent;backdrop-filter:blur(8px);border-radius:var(--borderRadius-sm);border:1px solid var(--codeBorder);cursor:pointer;transition:var(--transition-all);font-size:var(--text-sm);line-height:24px;color:currentColor;& svg[aria-live=polite]{display:none}}.copy-button svg{opacity:.5;transition:var(--transition-all)}pre .copy-button:hover svg,pre .copy-button:focus-visible svg{opacity:1}.copy-button svg{width:20px}.copy-button.clicked{opacity:1;color:var(--success);& svg[aria-live=polite]{display:block}}.copy-button.clicked svg{display:none;color:currentColor}#settings-modal-content{margin-top:10px}#settings-modal-content .hidden{display:none}#settings-modal-content .input{box-sizing:border-box;width:80%;padding:8px;font-size:var(--text-sm);background-color:var(--settingsInputBackground);color:var(--settingsInput);border:1px solid var(--settingsInputBorder);border-radius:var(--borderRadius-base);transition:var(--transition-all)}#settings-modal-content .input:focus{border-color:var(--main)}#settings-modal-content .input::placeholder{color:var(--gray400)}#settings-modal-content .switch-button-container{display:flex;align-items:center;justify-content:space-between;border-top:1px solid var(--settingsSectionBorder);padding:10px 0}#settings-modal-content .switch-button-container:first-of-type{border-top-style:none;padding-top:0}#settings-modal-content .switch-button-container>div>span{font-size:var(--text-md)}#settings-modal-content .switch-button-container>div>p{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:var(--text-sm);line-height:1.4;margin:0;padding-bottom:6px;padding-right:10px}#settings-modal-content .switch-button{position:relative;display:inline-block;flex-shrink:0;width:40px;height:20px;user-select:none;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox{appearance:none;position:absolute;display:block;width:20px;height:20px;border-radius:1000px;background-color:#91a4b7;border:3px solid #e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__bg{display:block;width:100%;height:100%;border-radius:1000px;background-color:#e5edf5;cursor:pointer;transition:var(--transition-all)}#settings-modal-content .switch-button__checkbox:checked{background-color:#fff;border-color:var(--main);transform:translate(100%)}#settings-modal-content .switch-button__checkbox:checked+.switch-button__bg{background-color:var(--main)}#settings-modal-content .switch-button__checkbox:focus{outline:0}#settings-modal-content .switch-button__checkbox:focus+.switch-button__bg{outline:2px solid var(--main);outline-offset:2px}#settings-modal-content .switch-button__checkbox:focus:not(:focus-visible)+.switch-button__bg{outline:0}#settings-modal-content .settings-select{cursor:pointer;position:relative;border:none;background-color:transparent;color:var(--textBody)}#settings-modal-content .settings-select option{color:initial}#toast{visibility:hidden;opacity:0;position:fixed;z-index:1000;right:10px;top:0;font-size:.9rem;min-width:3rem;padding:.7rem 1.2rem .7rem 2.8rem;text-align:left;font-weight:400;border-radius:var(--borderRadius-base);border:1px solid hsl(from var(--tipHeading) h s l / 15%);background-color:var(--tipBackground);color:var(--tipHeading);transition:opacity .4s ease-in-out,transform .3s ease-out;cursor:default}#toast:before{content:var(--icon-information);position:absolute;left:1rem;font-size:1.2rem;font-family:remixicon;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#toast.show{visibility:visible;opacity:1;transform:translateY(.75rem)}@media(prefers-reduced-motion:reduce){#toast{transition:none}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;user-select:none}@media print{.body-wrapper{display:block}.sidebar,.sidebar-button,.top-search{display:none}.content{padding-left:0;overflow:visible;left:0;width:100%}.summary-row{break-inside:avoid}#toast{display:none}.content-inner{padding:0}.content-inner .section-heading a.hover-link,.content-inner button.icon-action,.content-inner a.icon-action,.content-inner .bottom-actions{display:none}.footer p:first-of-type{display:none}.content-inner pre code.makeup{white-space:break-spaces;break-inside:avoid}}@media print{.page-cheatmd .content-inner{max-width:100%;width:100%;padding:0;font-size:.7em;& section:is(.col-2,.col-2-left,.col-3){column-gap:30px}& section.col-2{column-count:2}& section.col-2-left{display:grid}& section.col-3{column-count:3}}@scope(.page-cheatmd)to (.admonition){:scope .content-inner{& *{background-color:transparent!important;border-color:var(--gray400)!important}& h1{margin-top:0;margin-bottom:.5em}& h2.section-heading{font-weight:700;margin-top:1em;column-span:all}& section.h2{break-inside:avoid}& h3{font-weight:700;color:var(--mainDark)}& h3:after{height:2px;background-color:var(--gray400)}& section.h3{min-width:300px;break-inside:avoid}& h4{padding:.5em 0;border:none;font-weight:700;color:#000}.h2 p{padding-left:0;padding-right:0;border:none!important}& code{line-height:1.5em}.h2 table{font-variant-numeric:tabular-nums;break-inside:avoid}.h2 :is(th,td){vertical-align:top;padding-left:0;padding-right:0}.h2 thead{border-style:solid none;border-width:1px}.h2 tr{border-bottom:none}.h2 th{font-weight:700}.h2 li{padding-left:0;padding-right:0;vertical-align:middle;border-bottom:none}& pre:hover button.copy-button{display:none}& div.tooltip{display:none}& footer p:not(.built-using){display:none}}}}code.makeup .unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.makeup .hll{background-color:#ffc}.makeup .bp{color:#3465a4}.makeup .c,.makeup .c1,.makeup .ch,.makeup .cm,.makeup .cp,.makeup .cpf,.makeup .cs{color:#4d4d4d}.makeup .dl{color:#408200}.makeup .err{color:#a40000;border:#ef2929}.makeup .fm,.makeup .g{color:#4d4d4c}.makeup .gd{color:#a40000}.makeup .ge{color:#4d4d4c;font-style:italic}.makeup .gh{color:navy;font-weight:700}.makeup .gi{color:#00a000}.makeup .go{color:#4d4d4c;font-style:italic}.makeup .gp{color:#4d4d4d}.makeup .gr{color:#ef2929}.makeup .gs{color:#4d4d4c;font-weight:700}.makeup .gt{color:#a40000;font-weight:700}.makeup .gu{color:purple;font-weight:700}.makeup .il{color:#0000cf;font-weight:700}.makeup .k,.makeup .kc,.makeup .kd,.makeup .kn,.makeup .kp,.makeup .kr,.makeup .kt{color:#204a87}.makeup .l{color:#4d4d4c}.makeup .ld{color:#c00}.makeup .m,.makeup .mb,.makeup .mf,.makeup .mh,.makeup .mi,.makeup .mo{color:#2937ab}.makeup .n{color:#4d4d4c}.makeup .na{color:#8a7000}.makeup .nb{color:#204a87}.makeup .nc{color:#0000cf}.makeup .nd{color:#5c35cc;font-weight:700}.makeup .ne{color:#c00;font-weight:700}.makeup .nf{color:#b65800}.makeup .ni{color:#bc5400}.makeup .nl{color:#b65800}.makeup .nn{color:#4d4d4c}.makeup .no{color:#a06600}.makeup .nt{color:#204a87;font-weight:700}.makeup .nv,.makeup .nx{color:#4d4d4c}.makeup .o{color:#bc5400}.makeup .ow{color:#204a87}.makeup .p,.makeup .py{color:#4d4d4c}.makeup .s,.makeup .s1,.makeup .s2,.makeup .sa,.makeup .sb,.makeup .sc{color:#408200}.makeup .sd{color:#8f5902;font-style:italic}.makeup .se{color:#204a87}.makeup .sh{color:#408200}.makeup .si{color:#204a87}.makeup .sr{color:#c00}.makeup .ss{color:#a06600}.makeup .sx{color:#408200}.makeup .vc,.makeup .vg,.makeup .vi,.makeup .vm,.makeup .x{color:#4d4d4c}.dark .makeup{color:#dce1e6}.dark .makeup .hll{background-color:#49483e}.dark .makeup .bp{color:#dce1e6}.dark .makeup .c,.dark .makeup .c1,.dark .makeup .ch,.dark .makeup .cm,.dark .makeup .cp,.dark .makeup .cpf,.dark .makeup .cs{color:#969386}.dark .makeup .dl{color:#e6db74}.dark .makeup .err{color:#960050;background-color:#1e0010}.dark .makeup .fm{color:#a6e22e}.dark .makeup .gd{color:#ff5385}.dark .makeup .ge{font-style:italic}.dark .makeup .gi{color:#a6e22e}.dark .makeup .gp{color:#969386}.dark .makeup .gs{font-weight:700}.dark .makeup .gu{color:#969386}.dark .makeup .gt{color:#ff5385;font-weight:700}.dark .makeup .il{color:#ae81ff}.dark .makeup .k,.dark .makeup .kc,.dark .makeup .kd{color:#66d9ef}.dark .makeup .kn{color:#ff5385}.dark .makeup .kp,.dark .makeup .kr,.dark .makeup .kt{color:#66d9ef}.dark .makeup .l,.dark .makeup .ld,.dark .makeup .m,.dark .makeup .mb,.dark .makeup .mf,.dark .makeup .mh,.dark .makeup .mi,.dark .makeup .mo{color:#ae81ff}.dark .makeup .n{color:#dce1e6}.dark .makeup .na{color:#a6e22e}.dark .makeup .nb{color:#dce1e6}.dark .makeup .nc,.dark .makeup .nd,.dark .makeup .ne,.dark .makeup .nf{color:#a6e22e}.dark .makeup .ni,.dark .makeup .nl,.dark .makeup .nn{color:#dce1e6}.dark .makeup .no{color:#66d9ef}.dark .makeup .nt{color:#ff5385}.dark .makeup .nv{color:#dce1e6}.dark .makeup .nx{color:#a6e22e}.dark .makeup .o,.dark .makeup .ow{color:#ff5385}.dark .makeup .p,.dark .makeup .py{color:#dce1e6}.dark .makeup .s,.dark .makeup .s1,.dark .makeup .s2,.dark .makeup .sa,.dark .makeup .sb,.dark .makeup .sc,.dark .makeup .sd{color:#e6db74}.dark .makeup .se{color:#ae81ff}.dark .makeup .sh,.dark .makeup .si,.dark .makeup .sr,.dark .makeup .ss,.dark .makeup .sx{color:#e6db74}.dark .makeup .vc,.dark .makeup .vg,.dark .makeup .vi,.dark .makeup .vm{color:#dce1e6}.tabset{--borderWidth: 1px;--tabsetPadding: var(--baseLineHeight);margin:var(--baseLineHeight) 0;border:var(--borderWidth) solid var(--tabBorder);padding:0 var(--tabsetPadding);border-radius:var(--borderRadius-lg)}.tabset-tablist{display:flex;overflow:auto;scrollbar-width:thin;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--tabBorderTop)}.tabset-tab{padding:1.1rem var(--tabsetPadding);font-family:var(--sansFontFamily);color:var(--textColor);margin-right:calc(-1 * var(--borderWidth));background-color:transparent;border:0;box-shadow:none;cursor:pointer;border-bottom-width:2px;border-bottom-style:solid;border-bottom-color:transparent;transition:var(--transition-all)}:hover.tabset-tab{border-bottom-color:var(--tabBorderTop);color:var(--textHeaders)}.tabset-tab[aria-selected=true]{border-bottom-color:var(--mainLight);color:var(--textHeaders)}.tabset-tab[aria-selected=true]:focus-visible{background-color:var(--mainLight);border-color:var(--mainLight);color:var(--white)}@media screen and (max-width:768px){.tabset{--tabsetPadding: calc(var(--baseLineHeight) / 2)}.tabset-panel{padding-top:calc(var(--tabsetPadding) / 2);padding-bottom:calc(var(--tabsetPadding) / 2)}.tabset-panel pre,.tabset-panel blockquote,.tabset-panel section.admonition{margin-left:calc(-1 * var(--tabsetPadding))!important;margin-right:calc(-1 * var(--tabsetPadding))!important}.tabset-panel>pre code{border-left-width:0;border-right-width:0}}@media screen and (max-width:768px){.tabset-panel>:is(:first-child){&:is(table){margin:.5em 0}}}@media screen and (min-width:769px){.tabset-panel>:is(:first-child){&:is(blockquote,.admonition){margin-top:1.5em}&:is(p:has(img)){margin-top:1.25em}&:is(table){margin-top:.75em}}.tabset-panel>:is(:last-child){&:is(blockquote,.admonition){margin-bottom:1.5em}&:is(p:not(:has(img)),ul,ol){margin-bottom:1.25em}&:is(table){margin-bottom:.75em}}}body:not(.dark) .content-inner img[src*="#gh-dark-mode-only"],body.dark .content-inner img[src*="#gh-light-mode-only"]{display:none} -/*! Bundled license information: - -modern-normalize/modern-normalize.css: - (*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize *) -*/ diff --git a/doc/dist/lato-latin-400-normal-W7754I4D.woff2 b/doc/dist/lato-latin-400-normal-W7754I4D.woff2 deleted file mode 100644 index ff60934dd0ea9cdd93f961d2dfd1e158357f2a77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23580 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 QOFh<%JiLFgmWQ+r07g396aWAK diff --git a/doc/dist/lato-latin-700-normal-2XVSBPG4.woff2 b/doc/dist/lato-latin-700-normal-2XVSBPG4.woff2 deleted file mode 100644 index 11de83feaf0fb872c91fce89534fd4e2d8bbda28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23040 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 diff --git a/doc/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 b/doc/dist/lato-latin-ext-400-normal-N27NCBWW.woff2 deleted file mode 100644 index edb9fa6f492362a9ab1f1d674b8f6e6f4e2085d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5472 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 WhF)K4IGNT+hd8;|K8&%*Sz!P*WW1>W diff --git a/doc/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 b/doc/dist/lato-latin-ext-700-normal-Q2L5DVMW.woff2 deleted file mode 100644 index 2c8aaa866eee8bee09ee05a9809c2fda7cd87bd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5368 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 diff --git a/doc/dist/search_data-85BCB47E.js b/doc/dist/search_data-85BCB47E.js deleted file mode 100644 index a6e9abe..0000000 --- a/doc/dist/search_data-85BCB47E.js +++ /dev/null @@ -1 +0,0 @@ -searchData={"items":[{"type":"module","title":"nova_liveboard","doc":"Configuration helpers for the liveboard.\n\nAll settings live under the `nova_liveboard` application env:\n\n- `prefix` (default `\"/liveboard\"`) - the path the dashboard mounts on.\n- `refresh_ms` (default `2000`) - how often the polled SSE streams repaint.\n- `request_buffer` (default `200`) - how many recent requests the tracer keeps.","ref":"nova_liveboard.html"},{"type":"function","title":"nova_liveboard.prefix/0","doc":"The mount path, always as a binary with a leading slash.","ref":"nova_liveboard.html#prefix/0"},{"type":"function","title":"nova_liveboard.refresh_ms/0","doc":"Repaint interval for the polled (non-request) streams, in milliseconds.","ref":"nova_liveboard.html#refresh_ms/0"},{"type":"function","title":"nova_liveboard.request_buffer/0","doc":"How many completed requests the tracer ring buffer retains.","ref":"nova_liveboard.html#request_buffer/0"},{"type":"module","title":"nova_liveboard_action_controller","doc":"POST handlers for the Requests page controls (arm / disarm / clear deep trace).\n\nEach mutates `nova_liveboard_tracer` and returns a one-shot Datastar response:\na normal `{status, 200, ...}` reply whose body is SSE-framed patch events.\nNova's built-in `handle_status` sends it, and Datastar applies the patches to\nthe clicking client immediately. Other open clients converge via the tracer's\nown notifications to their Requests stream.","ref":"nova_liveboard_action_controller.html"},{"type":"function","title":"nova_liveboard_action_controller.clear/1","doc":"","ref":"nova_liveboard_action_controller.html#clear/1"},{"type":"function","title":"nova_liveboard_action_controller.trace_start/1","doc":"","ref":"nova_liveboard_action_controller.html#trace_start/1"},{"type":"function","title":"nova_liveboard_action_controller.trace_stop/1","doc":"","ref":"nova_liveboard_action_controller.html#trace_stop/1"},{"type":"module","title":"nova_liveboard_app","doc":"Application entry point.\n\nStarts the supervision tree (the request tracer) and registers the\n`{stream, ...}` Datastar SSE return-handler with Nova. The request-tracing\nplugin is *not* auto-registered: it is a global Nova plugin, so the host wires\nit into its own `{nova, [{plugins, ...}]}` config (see the README). The\ndashboard's pages work either way; only the Requests feed needs the plugin.","ref":"nova_liveboard_app.html"},{"type":"function","title":"nova_liveboard_app.start/2","doc":"","ref":"nova_liveboard_app.html#start/2"},{"type":"function","title":"nova_liveboard_app.stop/1","doc":"","ref":"nova_liveboard_app.html#stop/1"},{"type":"module","title":"nova_liveboard_data","doc":"","ref":"nova_liveboard_data.html"},{"type":"function","title":"nova_liveboard_data.collect_metrics/1","doc":"","ref":"nova_liveboard_data.html#collect_metrics/1"},{"type":"function","title":"nova_liveboard_data.ets_tables/0","doc":"","ref":"nova_liveboard_data.html#ets_tables/0"},{"type":"function","title":"nova_liveboard_data.format_bytes/1","doc":"","ref":"nova_liveboard_data.html#format_bytes/1"},{"type":"function","title":"nova_liveboard_data.format_number/1","doc":"","ref":"nova_liveboard_data.html#format_number/1"},{"type":"function","title":"nova_liveboard_data.format_uptime/1","doc":"","ref":"nova_liveboard_data.html#format_uptime/1"},{"type":"function","title":"nova_liveboard_data.kura_available/0","doc":"","ref":"nova_liveboard_data.html#kura_available/0"},{"type":"function","title":"nova_liveboard_data.kura_migration_status/1","doc":"","ref":"nova_liveboard_data.html#kura_migration_status/1"},{"type":"function","title":"nova_liveboard_data.kura_pool_stats/1","doc":"","ref":"nova_liveboard_data.html#kura_pool_stats/1"},{"type":"function","title":"nova_liveboard_data.kura_repo_info/1","doc":"","ref":"nova_liveboard_data.html#kura_repo_info/1"},{"type":"function","title":"nova_liveboard_data.kura_repos/0","doc":"","ref":"nova_liveboard_data.html#kura_repos/0"},{"type":"function","title":"nova_liveboard_data.kura_schemas/1","doc":"","ref":"nova_liveboard_data.html#kura_schemas/1"},{"type":"function","title":"nova_liveboard_data.port_info/0","doc":"","ref":"nova_liveboard_data.html#port_info/0"},{"type":"function","title":"nova_liveboard_data.running_applications/0","doc":"","ref":"nova_liveboard_data.html#running_applications/0"},{"type":"function","title":"nova_liveboard_data.scheduler_info/0","doc":"","ref":"nova_liveboard_data.html#scheduler_info/0"},{"type":"function","title":"nova_liveboard_data.sparkline_points/3","doc":"","ref":"nova_liveboard_data.html#sparkline_points/3"},{"type":"function","title":"nova_liveboard_data.supervision_tree/1","doc":"","ref":"nova_liveboard_data.html#supervision_tree/1"},{"type":"function","title":"nova_liveboard_data.system_info/0","doc":"","ref":"nova_liveboard_data.html#system_info/0"},{"type":"function","title":"nova_liveboard_data.top_processes/2","doc":"","ref":"nova_liveboard_data.html#top_processes/2"},{"type":"module","title":"nova_liveboard_html","doc":"Pure HTML rendering for the liveboard - \"BEAM mission control\".\n\nEvery function here takes already-gathered data (from `nova_liveboard_data`\nand `nova_liveboard_tracer`) and returns `iodata()`. No process state, no\nside effects beyond reading the static `nova_liveboard:prefix/0` config, so\nthe whole module is unit-testable in isolation.\n\n`page/4` renders the full document shell (vitals deck + instrument nav +\nmain stage). The per-region functions (`vitals_html/1`, `overview_html/1`,\n`processes_html/2`, ...) render the *inner* of a live region; the page\ncontroller uses them for the first paint and `nova_liveboard_sse` re-emits\nthem as Datastar patches as the VM changes.","ref":"nova_liveboard_html.html"},{"type":"function","title":"nova_liveboard_html.apps_html/1","doc":"Running applications table.","ref":"nova_liveboard_html.html#apps_html/1"},{"type":"function","title":"nova_liveboard_html.csp_headers/0","doc":"The dashboard's response headers. Strict CSP: everything is same-origin\n(self-hosted datastar.js, fonts, css), so any stray off-origin request fails\nloudly. `unsafe-eval` is required only for Datastar's `data-*` expression\nevaluation; the privacy-critical directives stay `'self'`.","ref":"nova_liveboard_html.html#csp_headers/0"},{"type":"function","title":"nova_liveboard_html.database_html/1","doc":"Kura repo pool panels.","ref":"nova_liveboard_html.html#database_html/1"},{"type":"function","title":"nova_liveboard_html.ets_html/1","doc":"ETS tables table.","ref":"nova_liveboard_html.html#ets_html/1"},{"type":"function","title":"nova_liveboard_html.html_escape/1","doc":"Escape `& < > \"` for safe interpolation into HTML.","ref":"nova_liveboard_html.html#html_escape/1"},{"type":"function","title":"nova_liveboard_html.metrics_html/1","doc":"Live sparklines for memory/IO/run-queue + scheduler utilisation bars.","ref":"nova_liveboard_html.html#metrics_html/1"},{"type":"function","title":"nova_liveboard_html.overview_html/1","doc":"System identity + memory breakdown + capacity panels.","ref":"nova_liveboard_html.html#overview_html/1"},{"type":"function","title":"nova_liveboard_html.page/4","doc":"Full HTML document: head + vitals deck + nav + the main stage.\n\n`StreamPath` is the SSE path the main region should subscribe to (so it\nrepaints live), or `none` for a static page (e.g. a request detail) that must\nnot be overwritten by a stream.","ref":"nova_liveboard_html.html#page/4"},{"type":"function","title":"nova_liveboard_html.ports_html/1","doc":"Open ports table.","ref":"nova_liveboard_html.html#ports_html/1"},{"type":"function","title":"nova_liveboard_html.processes_html/2","doc":"Top processes table, sorted by `SortBy`, with sort controls.","ref":"nova_liveboard_html.html#processes_html/2"},{"type":"function","title":"nova_liveboard_html.request_detail_html/1","doc":"Detail page for one captured request: timings + spawned-process tree.","ref":"nova_liveboard_html.html#request_detail_html/1"},{"type":"function","title":"nova_liveboard_html.request_row/1","doc":"A single request row (also emitted as a prepended SSE patch on arrival).","ref":"nova_liveboard_html.html#request_row/1"},{"type":"function","title":"nova_liveboard_html.requests_html/3","doc":"The live request feed. `Enabled` reflects whether the tracing plugin is\nregistered; when it is not, a setup hint is shown instead of an empty feed.\n`Trace` is the deep-trace arming state from `nova_liveboard_tracer`.","ref":"nova_liveboard_html.html#requests_html/3"},{"type":"function","title":"nova_liveboard_html.requests_toolbar_html/1","doc":"The arm/disarm/clear toolbar (patched on its own as trace state changes).","ref":"nova_liveboard_html.html#requests_toolbar_html/1"},{"type":"function","title":"nova_liveboard_html.schemas_html/1","doc":"Kura schema definitions (fields, associations, indexes).","ref":"nova_liveboard_html.html#schemas_html/1"},{"type":"function","title":"nova_liveboard_html.sup_tree_html/3","doc":"The supervision tree for `Active` (static process structure) + app chooser.","ref":"nova_liveboard_html.html#sup_tree_html/3"},{"type":"function","title":"nova_liveboard_html.vitals_html/1","doc":"The live header gauges: capacity vs limits + memory + uptime.","ref":"nova_liveboard_html.html#vitals_html/1"},{"type":"module","title":"nova_liveboard_page_controller","doc":"Nova controllers for the liveboard pages.\n\nEach page server-renders the full mission-control shell (`nova_liveboard_html:\npage/4`) with a first-paint snapshot, then a `data-init` stream takes over and\nrepaints the live region. `page_inner/2` is the single place that maps a page\nto its data + HTML; both these controllers and `nova_liveboard_sse` call it, so\nthe first paint and the live patches always agree.","ref":"nova_liveboard_page_controller.html"},{"type":"function","title":"nova_liveboard_page_controller.app_names/0","doc":"","ref":"nova_liveboard_page_controller.html#app_names/0"},{"type":"function","title":"nova_liveboard_page_controller.applications/1","doc":"","ref":"nova_liveboard_page_controller.html#applications/1"},{"type":"function","title":"nova_liveboard_page_controller.database/1","doc":"","ref":"nova_liveboard_page_controller.html#database/1"},{"type":"function","title":"nova_liveboard_page_controller.default_app/1","doc":"","ref":"nova_liveboard_page_controller.html#default_app/1"},{"type":"function","title":"nova_liveboard_page_controller.ets/1","doc":"","ref":"nova_liveboard_page_controller.html#ets/1"},{"type":"function","title":"nova_liveboard_page_controller.index/1","doc":"","ref":"nova_liveboard_page_controller.html#index/1"},{"type":"function","title":"nova_liveboard_page_controller.metrics/1","doc":"","ref":"nova_liveboard_page_controller.html#metrics/1"},{"type":"function","title":"nova_liveboard_page_controller.page_atom/1","doc":"Binary page name (from a route binding) to its page atom.","ref":"nova_liveboard_page_controller.html#page_atom/1"},{"type":"function","title":"nova_liveboard_page_controller.page_inner/2","doc":"Map a page to its freshly-gathered data, rendered to the live region inner.","ref":"nova_liveboard_page_controller.html#page_inner/2"},{"type":"function","title":"nova_liveboard_page_controller.ports/1","doc":"","ref":"nova_liveboard_page_controller.html#ports/1"},{"type":"function","title":"nova_liveboard_page_controller.processes/1","doc":"","ref":"nova_liveboard_page_controller.html#processes/1"},{"type":"function","title":"nova_liveboard_page_controller.request_show/1","doc":"","ref":"nova_liveboard_page_controller.html#request_show/1"},{"type":"function","title":"nova_liveboard_page_controller.requests/1","doc":"","ref":"nova_liveboard_page_controller.html#requests/1"},{"type":"function","title":"nova_liveboard_page_controller.schemas/1","doc":"","ref":"nova_liveboard_page_controller.html#schemas/1"},{"type":"function","title":"nova_liveboard_page_controller.supervisors/1","doc":"","ref":"nova_liveboard_page_controller.html#supervisors/1"},{"type":"module","title":"nova_liveboard_router","doc":"","ref":"nova_liveboard_router.html"},{"type":"function","title":"nova_liveboard_router.routes/1","doc":"","ref":"nova_liveboard_router.html#routes/1"},{"type":"module","title":"nova_liveboard_sse","doc":"The liveboard's live transport: one long-lived SSE stream per page region.\n\n`stream/1` is a normal Nova controller returning `{stream, Code, Headers,\nSource}`. That tuple is picked up by a return-handler we register\n(`handle_stream/3`), which `stream_reply`s the SSE headers and then **holds the\nconnection**, pushing `datastar:patch_elements/2` frames and never returning -\nso Nova's buffered `render_response` (which would reply again and crash) never\nruns. On client disconnect the next `stream_body` fails and the request process\nexits, dropping any tracer subscription. See novaframework/nova#387.\n\nTwo stream shapes:\n\n- **polled** (vitals/overview/processes/ets/applications/ports/supervisors/\n metrics): a `receive ... after RefreshMs` timer repaints the region. The\n metrics stream threads the `nova_liveboard_data:collect_metrics/1`\n accumulator through its loop so sparklines and scheduler deltas build up.\n- **event-driven** (requests): subscribes to `nova_liveboard_tracer` and\n prepends each request as it completes; no polling.\n\nThe handler is registered as an explicit `fun/3` because current Nova invokes\nreturn-handlers with 3 args, while its `{Mod, Fun}` form wraps to arity 4.","ref":"nova_liveboard_sse.html"},{"type":"function","title":"nova_liveboard_sse.handle_stream/3","doc":"","ref":"nova_liveboard_sse.html#handle_stream/3"},{"type":"function","title":"nova_liveboard_sse.register/0","doc":"","ref":"nova_liveboard_sse.html#register/0"},{"type":"function","title":"nova_liveboard_sse.stream/1","doc":"","ref":"nova_liveboard_sse.html#stream/1"},{"type":"module","title":"nova_liveboard_sup","doc":"Top supervisor: owns the request tracer.","ref":"nova_liveboard_sup.html"},{"type":"function","title":"nova_liveboard_sup.init/1","doc":"","ref":"nova_liveboard_sup.html#init/1"},{"type":"function","title":"nova_liveboard_sup.start_link/0","doc":"","ref":"nova_liveboard_sup.html#start_link/0"},{"type":"module","title":"nova_liveboard_trace_plugin","doc":"Nova plugin that feeds the liveboard's request tracer.\n\nRegister it as a global plugin in your Nova config to capture every request\nhandled by the node:\n\n {nova, [\n {plugins, [\n {pre_request, nova_liveboard_trace_plugin, #{}},\n {post_request, nova_liveboard_trace_plugin, #{}}\n ]}\n ]}.\n\n`pre_request/4` stamps the request with an id, start time, reductions baseline\nand the resolved handler MFA (from `Env#{callback}`), and - when deep trace is\narmed - turns on scoped process tracing of the handling process.\n`post_request/4` computes duration / reductions / peak memory / status and\nhands the summary to `nova_liveboard_tracer`. The whole chain runs in one\nprocess, so the marker stashed in `Req` survives from pre to post.\n\nThe dashboard's own routes are skipped so it never traces itself (its SSE\nstreams never return, and would otherwise never finalise).","ref":"nova_liveboard_trace_plugin.html"},{"type":"function","title":"nova_liveboard_trace_plugin.init/0","doc":"","ref":"nova_liveboard_trace_plugin.html#init/0"},{"type":"function","title":"nova_liveboard_trace_plugin.plugin_info/0","doc":"","ref":"nova_liveboard_trace_plugin.html#plugin_info/0"},{"type":"function","title":"nova_liveboard_trace_plugin.post_request/4","doc":"","ref":"nova_liveboard_trace_plugin.html#post_request/4"},{"type":"function","title":"nova_liveboard_trace_plugin.pre_request/4","doc":"","ref":"nova_liveboard_trace_plugin.html#pre_request/4"},{"type":"module","title":"nova_liveboard_tracer","doc":"Request store and deep-trace collector for the liveboard.\n\nTwo jobs in one `gen_server`:\n\n1. **Store** - a capped, newest-first ring of recently completed requests\n (`nova_liveboard:request_buffer/0` deep). `nova_liveboard_trace_plugin`\n calls `finalize/2` from each request's `post_request` hook; SSE feeds\n `subscribe/0` to it for live updates.\n\n2. **Trace collector** - when deep trace is armed (`start_trace/1`), the next\n N requests have their handling process traced with\n `erlang:trace(self(), true, [procs, set_on_spawn, {tracer, Pid}])` (the\n plugin enables it; this server is the `Pid`). It receives\n `{trace, Parent, spawn, Child, MFA}` messages, attributes each spawned\n process to the owning request by walking the parent chain, and attaches the\n resulting spawned-process tree to the request record on `finalize/2`.\n\nThe dashboard works without any of this; only the Requests page needs the\nplugin registered.","ref":"nova_liveboard_tracer.html"},{"type":"function","title":"nova_liveboard_tracer.active/0","doc":"Whether any request has been captured (a proxy for 'plugin is wired up').","ref":"nova_liveboard_tracer.html#active/0"},{"type":"function","title":"nova_liveboard_tracer.clear/0","doc":"","ref":"nova_liveboard_tracer.html#clear/0"},{"type":"function","title":"nova_liveboard_tracer.finalize/2","doc":"Record a completed request; attaches the spawned tree if it was traced.","ref":"nova_liveboard_tracer.html#finalize/2"},{"type":"function","title":"nova_liveboard_tracer.get/1","doc":"","ref":"nova_liveboard_tracer.html#get/1"},{"type":"function","title":"nova_liveboard_tracer.handle_call/3","doc":"","ref":"nova_liveboard_tracer.html#handle_call/3"},{"type":"function","title":"nova_liveboard_tracer.handle_cast/2","doc":"","ref":"nova_liveboard_tracer.html#handle_cast/2"},{"type":"function","title":"nova_liveboard_tracer.handle_info/2","doc":"","ref":"nova_liveboard_tracer.html#handle_info/2"},{"type":"function","title":"nova_liveboard_tracer.init/1","doc":"","ref":"nova_liveboard_tracer.html#init/1"},{"type":"function","title":"nova_liveboard_tracer.maybe_arm/2","doc":"Ask whether the request `ReqId` handled by `RootPid` should be deep-traced.\n\nReturns `trace` (and registers the root pid) when deep trace is armed, else\n`no_trace`. The caller (the request process) is responsible for calling\n`erlang:trace/3` on itself when `trace` is returned.","ref":"nova_liveboard_tracer.html#maybe_arm/2"},{"type":"function","title":"nova_liveboard_tracer.recent/0","doc":"","ref":"nova_liveboard_tracer.html#recent/0"},{"type":"function","title":"nova_liveboard_tracer.start_link/0","doc":"","ref":"nova_liveboard_tracer.html#start_link/0"},{"type":"function","title":"nova_liveboard_tracer.start_trace/1","doc":"","ref":"nova_liveboard_tracer.html#start_trace/1"},{"type":"function","title":"nova_liveboard_tracer.stop_trace/0","doc":"","ref":"nova_liveboard_tracer.html#stop_trace/0"},{"type":"function","title":"nova_liveboard_tracer.subscribe/0","doc":"Register the caller for live request + trace-state notifications.","ref":"nova_liveboard_tracer.html#subscribe/0"},{"type":"function","title":"nova_liveboard_tracer.trace_state/0","doc":"","ref":"nova_liveboard_tracer.html#trace_state/0"}],"proglang":"erlang","content_type":"text/markdown","producer":{"name":"ex_doc","version":"0.40.3"}} \ No newline at end of file diff --git a/doc/dist/sidebar_items-72351220.js b/doc/dist/sidebar_items-72351220.js deleted file mode 100644 index c8e7f92..0000000 --- a/doc/dist/sidebar_items-72351220.js +++ /dev/null @@ -1 +0,0 @@ -sidebarNodes={"modules":[{"id":"nova_liveboard","deprecated":false,"group":"","title":"nova_liveboard","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"prefix/0","deprecated":false,"title":"prefix()","anchor":"prefix/0"},{"id":"refresh_ms/0","deprecated":false,"title":"refresh_ms()","anchor":"refresh_ms/0"},{"id":"request_buffer/0","deprecated":false,"title":"request_buffer()","anchor":"request_buffer/0"}],"key":"functions"}]},{"id":"nova_liveboard_action_controller","deprecated":false,"group":"","title":"nova_liveboard_action_controller","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"clear/1","deprecated":false,"title":"clear(Req)","anchor":"clear/1"},{"id":"trace_start/1","deprecated":false,"title":"trace_start(Req)","anchor":"trace_start/1"},{"id":"trace_stop/1","deprecated":false,"title":"trace_stop(Req)","anchor":"trace_stop/1"}],"key":"functions"}]},{"id":"nova_liveboard_app","deprecated":false,"group":"","title":"nova_liveboard_app","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"start/2","deprecated":false,"title":"start(StartType, StartArgs)","anchor":"start/2"},{"id":"stop/1","deprecated":false,"title":"stop(State)","anchor":"stop/1"}],"key":"functions"}]},{"id":"nova_liveboard_data","deprecated":false,"group":"","title":"nova_liveboard_data","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"collect_metrics/1","deprecated":false,"title":"collect_metrics(State)","anchor":"collect_metrics/1"},{"id":"ets_tables/0","deprecated":false,"title":"ets_tables()","anchor":"ets_tables/0"},{"id":"format_bytes/1","deprecated":false,"title":"format_bytes(Bytes)","anchor":"format_bytes/1"},{"id":"format_number/1","deprecated":false,"title":"format_number(N)","anchor":"format_number/1"},{"id":"format_uptime/1","deprecated":false,"title":"format_uptime(Ms)","anchor":"format_uptime/1"},{"id":"kura_available/0","deprecated":false,"title":"kura_available()","anchor":"kura_available/0"},{"id":"kura_migration_status/1","deprecated":false,"title":"kura_migration_status(RepoMod)","anchor":"kura_migration_status/1"},{"id":"kura_pool_stats/1","deprecated":false,"title":"kura_pool_stats(PoolName)","anchor":"kura_pool_stats/1"},{"id":"kura_repo_info/1","deprecated":false,"title":"kura_repo_info(RepoMod)","anchor":"kura_repo_info/1"},{"id":"kura_repos/0","deprecated":false,"title":"kura_repos()","anchor":"kura_repos/0"},{"id":"kura_schemas/1","deprecated":false,"title":"kura_schemas(RepoMod)","anchor":"kura_schemas/1"},{"id":"port_info/0","deprecated":false,"title":"port_info()","anchor":"port_info/0"},{"id":"running_applications/0","deprecated":false,"title":"running_applications()","anchor":"running_applications/0"},{"id":"scheduler_info/0","deprecated":false,"title":"scheduler_info()","anchor":"scheduler_info/0"},{"id":"sparkline_points/3","deprecated":false,"title":"sparkline_points(Values, Width, Height)","anchor":"sparkline_points/3"},{"id":"supervision_tree/1","deprecated":false,"title":"supervision_tree(AppName)","anchor":"supervision_tree/1"},{"id":"system_info/0","deprecated":false,"title":"system_info()","anchor":"system_info/0"},{"id":"top_processes/2","deprecated":false,"title":"top_processes(SortBy, Limit)","anchor":"top_processes/2"}],"key":"functions"}]},{"id":"nova_liveboard_html","deprecated":false,"group":"","title":"nova_liveboard_html","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"apps_html/1","deprecated":false,"title":"apps_html(Apps)","anchor":"apps_html/1"},{"id":"csp_headers/0","deprecated":false,"title":"csp_headers()","anchor":"csp_headers/0"},{"id":"database_html/1","deprecated":false,"title":"database_html/1","anchor":"database_html/1"},{"id":"ets_html/1","deprecated":false,"title":"ets_html(Tables)","anchor":"ets_html/1"},{"id":"html_escape/1","deprecated":false,"title":"html_escape/1","anchor":"html_escape/1"},{"id":"metrics_html/1","deprecated":false,"title":"metrics_html(M)","anchor":"metrics_html/1"},{"id":"overview_html/1","deprecated":false,"title":"overview_html(Sys)","anchor":"overview_html/1"},{"id":"page/4","deprecated":false,"title":"page(Active, StreamPath, VitalsInner, MainInner)","anchor":"page/4"},{"id":"ports_html/1","deprecated":false,"title":"ports_html(Ports)","anchor":"ports_html/1"},{"id":"processes_html/2","deprecated":false,"title":"processes_html(Procs, SortBy)","anchor":"processes_html/2"},{"id":"request_detail_html/1","deprecated":false,"title":"request_detail_html/1","anchor":"request_detail_html/1"},{"id":"request_row/1","deprecated":false,"title":"request_row(R)","anchor":"request_row/1"},{"id":"requests_html/3","deprecated":false,"title":"requests_html/3","anchor":"requests_html/3"},{"id":"requests_toolbar_html/1","deprecated":false,"title":"requests_toolbar_html(Trace)","anchor":"requests_toolbar_html/1"},{"id":"schemas_html/1","deprecated":false,"title":"schemas_html/1","anchor":"schemas_html/1"},{"id":"sup_tree_html/3","deprecated":false,"title":"sup_tree_html(Apps, Active, Result)","anchor":"sup_tree_html/3"},{"id":"vitals_html/1","deprecated":false,"title":"vitals_html(Sys)","anchor":"vitals_html/1"}],"key":"functions"}]},{"id":"nova_liveboard_page_controller","deprecated":false,"group":"","title":"nova_liveboard_page_controller","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"app_names/0","deprecated":false,"title":"app_names()","anchor":"app_names/0"},{"id":"applications/1","deprecated":false,"title":"applications(Req)","anchor":"applications/1"},{"id":"database/1","deprecated":false,"title":"database(Req)","anchor":"database/1"},{"id":"default_app/1","deprecated":false,"title":"default_app(Names)","anchor":"default_app/1"},{"id":"ets/1","deprecated":false,"title":"ets(Req)","anchor":"ets/1"},{"id":"index/1","deprecated":false,"title":"index(Req)","anchor":"index/1"},{"id":"metrics/1","deprecated":false,"title":"metrics(Req)","anchor":"metrics/1"},{"id":"page_atom/1","deprecated":false,"title":"page_atom/1","anchor":"page_atom/1"},{"id":"page_inner/2","deprecated":false,"title":"page_inner/2","anchor":"page_inner/2"},{"id":"ports/1","deprecated":false,"title":"ports(Req)","anchor":"ports/1"},{"id":"processes/1","deprecated":false,"title":"processes(Req)","anchor":"processes/1"},{"id":"request_show/1","deprecated":false,"title":"request_show(Req)","anchor":"request_show/1"},{"id":"requests/1","deprecated":false,"title":"requests(Req)","anchor":"requests/1"},{"id":"schemas/1","deprecated":false,"title":"schemas(Req)","anchor":"schemas/1"},{"id":"supervisors/1","deprecated":false,"title":"supervisors(Req)","anchor":"supervisors/1"}],"key":"functions"}]},{"id":"nova_liveboard_router","deprecated":false,"group":"","title":"nova_liveboard_router","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"routes/1","deprecated":false,"title":"routes(Env)","anchor":"routes/1"}],"key":"functions"}]},{"id":"nova_liveboard_sse","deprecated":false,"group":"","title":"nova_liveboard_sse","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"handle_stream/3","deprecated":false,"title":"handle_stream/3","anchor":"handle_stream/3"},{"id":"register/0","deprecated":false,"title":"register()","anchor":"register/0"},{"id":"stream/1","deprecated":false,"title":"stream(Req)","anchor":"stream/1"}],"key":"functions"}]},{"id":"nova_liveboard_sup","deprecated":false,"group":"","title":"nova_liveboard_sup","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"init/1","deprecated":false,"title":"init/1","anchor":"init/1"},{"id":"start_link/0","deprecated":false,"title":"start_link()","anchor":"start_link/0"}],"key":"functions"}]},{"id":"nova_liveboard_trace_plugin","deprecated":false,"group":"","title":"nova_liveboard_trace_plugin","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"init/0","deprecated":false,"title":"init()","anchor":"init/0"},{"id":"plugin_info/0","deprecated":false,"title":"plugin_info()","anchor":"plugin_info/0"},{"id":"post_request/4","deprecated":false,"title":"post_request(Req, Env, Opts, State)","anchor":"post_request/4"},{"id":"pre_request/4","deprecated":false,"title":"pre_request(Req, Env, Opts, State)","anchor":"pre_request/4"}],"key":"functions"}]},{"id":"nova_liveboard_tracer","deprecated":false,"group":"","title":"nova_liveboard_tracer","sections":[],"nodeGroups":[{"name":"Functions","nodes":[{"id":"active/0","deprecated":false,"title":"active()","anchor":"active/0"},{"id":"clear/0","deprecated":false,"title":"clear()","anchor":"clear/0"},{"id":"finalize/2","deprecated":false,"title":"finalize(ReqId, Summary)","anchor":"finalize/2"},{"id":"get/1","deprecated":false,"title":"get(ReqId)","anchor":"get/1"},{"id":"handle_call/3","deprecated":false,"title":"handle_call/3","anchor":"handle_call/3"},{"id":"handle_cast/2","deprecated":false,"title":"handle_cast/2","anchor":"handle_cast/2"},{"id":"handle_info/2","deprecated":false,"title":"handle_info/2","anchor":"handle_info/2"},{"id":"init/1","deprecated":false,"title":"init/1","anchor":"init/1"},{"id":"maybe_arm/2","deprecated":false,"title":"maybe_arm(ReqId, RootPid)","anchor":"maybe_arm/2"},{"id":"recent/0","deprecated":false,"title":"recent()","anchor":"recent/0"},{"id":"start_link/0","deprecated":false,"title":"start_link()","anchor":"start_link/0"},{"id":"start_trace/1","deprecated":false,"title":"start_trace(N)","anchor":"start_trace/1"},{"id":"stop_trace/0","deprecated":false,"title":"stop_trace()","anchor":"stop_trace/0"},{"id":"subscribe/0","deprecated":false,"title":"subscribe()","anchor":"subscribe/0"},{"id":"trace_state/0","deprecated":false,"title":"trace_state()","anchor":"trace_state/0"}],"key":"functions"}]}],"extras":[{"id":"api-reference","group":"","title":"API Reference","headers":[{"id":"Modules","anchor":"modules"}]}],"tasks":[]} \ No newline at end of file diff --git a/doc/index.html b/doc/index.html deleted file mode 100644 index 929106f..0000000 --- a/doc/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - nova_liveboard v0.0.0+build.11.ref1019ff1 — Documentation - - - - - diff --git a/doc/nova_liveboard.epub b/doc/nova_liveboard.epub deleted file mode 100644 index ea18d61b36a3fca35ec54605d1e03a60dd41d48e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28896 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¬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 diff --git a/doc/nova_liveboard.html b/doc/nova_liveboard.html deleted file mode 100644 index 4651a0d..0000000 --- a/doc/nova_liveboard.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - - - - - - - nova_liveboard — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

Configuration helpers for the liveboard.

All settings live under the nova_liveboard application env:

  • prefix (default "/liveboard") - the path the dashboard mounts on.
  • refresh_ms (default 2000) - how often the polled SSE streams repaint.
  • request_buffer (default 200) - how many recent requests the tracer keeps.
-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- -
-
- prefix() - -
- -

The mount path, always as a binary with a leading slash.

- -
- -
-
- refresh_ms() - -
- -

Repaint interval for the polled (non-request) streams, in milliseconds.

- -
- -
- - -

How many completed requests the tracer ring buffer retains.

- -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

prefix()

- - -
-
- -
- -
- -
-spec prefix() -> binary().
- -
- -

The mount path, always as a binary with a leading slash.

-
-
-
- -
- - - -
-

refresh_ms()

- - -
-
- -
- -
- -
-spec refresh_ms() -> pos_integer().
- -
- -

Repaint interval for the polled (non-request) streams, in milliseconds.

-
-
-
- -
- - - -
-

request_buffer()

- - -
-
- -
- -
- -
-spec request_buffer() -> pos_integer().
- -
- -

How many completed requests the tracer ring buffer retains.

-
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_action_controller.html b/doc/nova_liveboard_action_controller.html deleted file mode 100644 index ca3d25f..0000000 --- a/doc/nova_liveboard_action_controller.html +++ /dev/null @@ -1,264 +0,0 @@ - - - - - - - - - - - nova_liveboard_action_controller — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_action_controller - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

POST handlers for the Requests page controls (arm / disarm / clear deep trace).

Each mutates nova_liveboard_tracer and returns a one-shot Datastar response: -a normal {status, 200, ...} reply whose body is SSE-framed patch events. -Nova's built-in handle_status sends it, and Datastar applies the patches to -the clicking client immediately. Other open clients converge via the tracer's -own notifications to their Requests stream.

-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- -
-
- clear(Req) - -
- -
- -
- - -
- -
- - -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

clear(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

trace_start(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

trace_stop(Req)

- - -
-
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_app.html b/doc/nova_liveboard_app.html deleted file mode 100644 index f2f45b9..0000000 --- a/doc/nova_liveboard_app.html +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - - - - nova_liveboard_app — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_app - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

Application entry point.

Starts the supervision tree (the request tracer) and registers the -{stream, ...} Datastar SSE return-handler with Nova. The request-tracing -plugin is not auto-registered: it is a global Nova plugin, so the host wires -it into its own {nova, [{plugins, ...}]} config (see the README). The -dashboard's pages work either way; only the Requests feed needs the plugin.

-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- - - -
-
- stop(State) - -
- -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

start(StartType, StartArgs)

- - -
-
- -
- - -
-
-
- -
- - - -
-

stop(State)

- - -
-
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_data.html b/doc/nova_liveboard_data.html deleted file mode 100644 index 6add7c0..0000000 --- a/doc/nova_liveboard_data.html +++ /dev/null @@ -1,753 +0,0 @@ - - - - - - - - - - - nova_liveboard_data — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_data - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - -
- - -
- -
-

- - - - Summary -

-
-

- Functions -

- - - -
-
- ets_tables() - -
- -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- - - - - - - -
-
- kura_repos() - -
- -
- -
- - -
- -
-
- port_info() - -
- -
- - - -
- - -
- - - - - -
- - -
- - - -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

collect_metrics(State)

- - -
-
- -
- -
- -
-spec collect_metrics(undefined | map()) -> map().
- -
- - -
-
-
- -
- - - -
-

ets_tables()

- - -
-
- -
- -
- -
-spec ets_tables() -> [map()].
- -
- - -
-
-
- -
- - - -
-

format_bytes(Bytes)

- - -
-
- -
- -
- -
-spec format_bytes(non_neg_integer()) -> binary().
- -
- - -
-
-
- -
- - - -
-

format_number(N)

- - -
-
- -
- -
- -
-spec format_number(non_neg_integer()) -> binary().
- -
- - -
-
-
- -
- - - -
-

format_uptime(Ms)

- - -
-
- -
- -
- -
-spec format_uptime(non_neg_integer()) -> binary().
- -
- - -
-
-
- -
- - - -
-

kura_available()

- - -
-
- -
- -
- -
-spec kura_available() -> boolean().
- -
- - -
-
-
- -
- - - -
-

kura_migration_status(RepoMod)

- - -
-
- -
- -
- -
-spec kura_migration_status(module()) -> [map()].
- -
- - -
-
-
- -
- - - -
-

kura_pool_stats(PoolName)

- - -
-
- -
- -
- -
-spec kura_pool_stats(atom()) -> map().
- -
- - -
-
-
- -
- - - -
-

kura_repo_info(RepoMod)

- - -
-
- -
- -
- -
-spec kura_repo_info(module()) -> map().
- -
- - -
-
-
- -
- - - -
-

kura_repos()

- - -
-
- -
- -
- -
-spec kura_repos() -> [module()].
- -
- - -
-
-
- -
- - - -
-

kura_schemas(RepoMod)

- - -
-
- -
- -
- -
-spec kura_schemas(module()) -> [map()].
- -
- - -
-
-
- -
- - - -
-

port_info()

- - -
-
- -
- -
- -
-spec port_info() -> [map()].
- -
- - -
-
-
- -
- - - -
-

running_applications()

- - -
-
- -
- -
- -
-spec running_applications() -> [map()].
- -
- - -
-
-
- -
- - - -
-

scheduler_info()

- - -
-
- -
- -
- -
-spec scheduler_info() -> [map()].
- -
- - -
-
-
- -
- - - -
-

sparkline_points(Values, Width, Height)

- - -
-
- -
- -
- -
-spec sparkline_points(list(), number(), number()) -> binary().
- -
- - -
-
-
- -
- - - -
-

supervision_tree(AppName)

- - -
-
- -
- -
- -
-spec supervision_tree(atom()) -> {ok, list()} | {error, term()}.
- -
- - -
-
-
- -
- - - -
-

system_info()

- - -
-
- -
- -
- -
-spec system_info() -> map().
- -
- - -
-
-
- -
- - - -
-

top_processes(SortBy, Limit)

- - -
-
- -
- -
- -
-spec top_processes(SortBy, Limit) -> [map()]
-                       when SortBy :: memory | reductions | message_queue_len, Limit :: pos_integer().
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_html.html b/doc/nova_liveboard_html.html deleted file mode 100644 index febfda6..0000000 --- a/doc/nova_liveboard_html.html +++ /dev/null @@ -1,779 +0,0 @@ - - - - - - - - - - - nova_liveboard_html — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_html - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

Pure HTML rendering for the liveboard - "BEAM mission control".

Every function here takes already-gathered data (from nova_liveboard_data -and nova_liveboard_tracer) and returns iodata(). No process state, no -side effects beyond reading the static nova_liveboard:prefix/0 config, so -the whole module is unit-testable in isolation.

page/4 renders the full document shell (vitals deck + instrument nav + -main stage). The per-region functions (vitals_html/1, overview_html/1, -processes_html/2, ...) render the inner of a live region; the page -controller uses them for the first paint and nova_liveboard_sse re-emits -them as Datastar patches as the VM changes.

-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- -
- - -

Running applications table.

- -
- -
- - -

The dashboard's response headers. Strict CSP: everything is same-origin -(self-hosted datastar.js, fonts, css), so any stray off-origin request fails -loudly. unsafe-eval is required only for Datastar's data-* expression -evaluation; the privacy-critical directives stay 'self'.

- -
- -
- - -

Kura repo pool panels.

- -
- -
- - -

ETS tables table.

- -
- -
- - -

Escape & < > " for safe interpolation into HTML.

- -
- -
- - -

Live sparklines for memory/IO/run-queue + scheduler utilisation bars.

- -
- -
- - -

System identity + memory breakdown + capacity panels.

- -
- -
- - -

Full HTML document: head + vitals deck + nav + the main stage.

- -
- -
- - -

Open ports table.

- -
- -
- - -

Top processes table, sorted by SortBy, with sort controls.

- -
- -
- - -

Detail page for one captured request: timings + spawned-process tree.

- -
- -
- - -

A single request row (also emitted as a prepended SSE patch on arrival).

- -
- -
- - -

The live request feed. Enabled reflects whether the tracing plugin is -registered; when it is not, a setup hint is shown instead of an empty feed. -Trace is the deep-trace arming state from nova_liveboard_tracer.

- -
- -
- - -

The arm/disarm/clear toolbar (patched on its own as trace state changes).

- -
- -
- - -

Kura schema definitions (fields, associations, indexes).

- -
- -
- - -

The supervision tree for Active (static process structure) + app chooser.

- -
- -
- - -

The live header gauges: capacity vs limits + memory + uptime.

- -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

apps_html(Apps)

- - -
-
- -
- -
- -
-spec apps_html([map()]) -> iodata().
- -
- -

Running applications table.

-
-
-
- -
- - - -
-

csp_headers()

- - -
-
- -
- -
- -
-spec csp_headers() -> map().
- -
- -

The dashboard's response headers. Strict CSP: everything is same-origin -(self-hosted datastar.js, fonts, css), so any stray off-origin request fails -loudly. unsafe-eval is required only for Datastar's data-* expression -evaluation; the privacy-critical directives stay 'self'.

-
-
-
- -
- - - -
-

database_html/1

- - -
-
- -
- -
- -
-spec database_html([map()]) -> iodata().
- -
- -

Kura repo pool panels.

-
-
-
- -
- - - -
-

ets_html(Tables)

- - -
-
- -
- -
- -
-spec ets_html([map()]) -> iodata().
- -
- -

ETS tables table.

-
-
-
- -
- - - -
-

html_escape/1

- - -
-
- -
- -
- -
-spec html_escape(iodata() | atom() | integer()) -> binary().
- -
- -

Escape & < > " for safe interpolation into HTML.

-
-
-
- -
- - - -
-

metrics_html(M)

- - -
-
- -
- -
- -
-spec metrics_html(map()) -> iodata().
- -
- -

Live sparklines for memory/IO/run-queue + scheduler utilisation bars.

-
-
-
- -
- - - -
-

overview_html(Sys)

- - -
-
- -
- -
- -
-spec overview_html(map()) -> iodata().
- -
- -

System identity + memory breakdown + capacity panels.

-
-
-
- -
- - - -
-

page(Active, StreamPath, VitalsInner, MainInner)

- - -
-
- -
- -
- -
-spec page(atom(), binary() | none, iodata(), iodata()) -> iodata().
- -
- -

Full HTML document: head + vitals deck + nav + the main stage.

StreamPath is the SSE path the main region should subscribe to (so it -repaints live), or none for a static page (e.g. a request detail) that must -not be overwritten by a stream.

-
-
-
- -
- - - -
-

ports_html(Ports)

- - -
-
- -
- -
- -
-spec ports_html([map()]) -> iodata().
- -
- -

Open ports table.

-
-
-
- -
- - - -
-

processes_html(Procs, SortBy)

- - -
-
- -
- -
- -
-spec processes_html([map()], atom()) -> iodata().
- -
- -

Top processes table, sorted by SortBy, with sort controls.

-
-
-
- -
- - - -
-

request_detail_html/1

- - -
-
- -
- -
- -
-spec request_detail_html(map() | undefined) -> iodata().
- -
- -

Detail page for one captured request: timings + spawned-process tree.

-
-
-
- -
- - - -
-

request_row(R)

- - -
-
- -
- -
- -
-spec request_row(map()) -> iodata().
- -
- -

A single request row (also emitted as a prepended SSE patch on arrival).

-
-
-
- -
- - - -
-

requests_html/3

- - -
-
- -
- -
- -
-spec requests_html([map()], map(), boolean()) -> iodata().
- -
- -

The live request feed. Enabled reflects whether the tracing plugin is -registered; when it is not, a setup hint is shown instead of an empty feed. -Trace is the deep-trace arming state from nova_liveboard_tracer.

-
-
-
- -
- - - -
-

requests_toolbar_html(Trace)

- - -
-
- -
- -
- -
-spec requests_toolbar_html(map()) -> iodata().
- -
- -

The arm/disarm/clear toolbar (patched on its own as trace state changes).

-
-
-
- -
- - - -
-

schemas_html/1

- - -
-
- -
- -
- -
-spec schemas_html([map()]) -> iodata().
- -
- -

Kura schema definitions (fields, associations, indexes).

-
-
-
- -
- - - -
-

sup_tree_html(Apps, Active, Result)

- - -
-
- -
- -
- -
-spec sup_tree_html([atom()], atom(), {ok, list()} | {error, term()}) -> iodata().
- -
- -

The supervision tree for Active (static process structure) + app chooser.

-
-
-
- -
- - - -
-

vitals_html(Sys)

- - -
-
- -
- -
- -
-spec vitals_html(map()) -> iodata().
- -
- -

The live header gauges: capacity vs limits + memory + uptime.

-
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_page_controller.html b/doc/nova_liveboard_page_controller.html deleted file mode 100644 index 113d500..0000000 --- a/doc/nova_liveboard_page_controller.html +++ /dev/null @@ -1,603 +0,0 @@ - - - - - - - - - - - nova_liveboard_page_controller — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_page_controller - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

Nova controllers for the liveboard pages.

Each page server-renders the full mission-control shell (nova_liveboard_html: page/4) with a first-paint snapshot, then a data-init stream takes over and -repaints the live region. page_inner/2 is the single place that maps a page -to its data + HTML; both these controllers and nova_liveboard_sse call it, so -the first paint and the live patches always agree.

-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- -
-
- app_names() - -
- -
- -
- - -
- -
- - -
- -
- - -
- -
-
- ets(Req) - -
- -
- -
-
- index(Req) - -
- -
- -
-
- metrics(Req) - -
- -
- -
-
- page_atom/1 - -
- -

Binary page name (from a route binding) to its page atom.

- -
- -
-
- page_inner/2 - -
- -

Map a page to its freshly-gathered data, rendered to the live region inner.

- -
- -
-
- ports(Req) - -
- -
- -
- - -
- -
- - -
- -
- - -
- -
-
- schemas(Req) - -
- -
- -
- - -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

app_names()

- - -
-
- -
- -
- -
-spec app_names() -> [atom()].
- -
- - -
-
-
- -
- - - -
-

applications(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

database(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

default_app(Names)

- - -
-
- -
- -
- -
-spec default_app([atom()]) -> atom().
- -
- - -
-
-
- -
- - - -
-

ets(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

index(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

metrics(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

page_atom/1

- - -
-
- -
- -
- -
-spec page_atom(binary()) -> atom().
- -
- -

Binary page name (from a route binding) to its page atom.

-
-
-
- -
- - - -
-

page_inner/2

- - -
-
- -
- -
- -
-spec page_inner(atom(), term()) -> iodata().
- -
- -

Map a page to its freshly-gathered data, rendered to the live region inner.

-
-
-
- -
- - - -
-

ports(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

processes(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

request_show(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

requests(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

schemas(Req)

- - -
-
- -
- - -
-
-
- -
- - - -
-

supervisors(Req)

- - -
-
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_router.html b/doc/nova_liveboard_router.html deleted file mode 100644 index d94673e..0000000 --- a/doc/nova_liveboard_router.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - - - nova_liveboard_router — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_router - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - -
- - -
- -
-

- - - - Summary -

-
-

- Functions -

- -
-
- routes(Env) - -
- -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

routes(Env)

- - -
-
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_sse.html b/doc/nova_liveboard_sse.html deleted file mode 100644 index 55f999f..0000000 --- a/doc/nova_liveboard_sse.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - - - - - nova_liveboard_sse — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_sse - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

The liveboard's live transport: one long-lived SSE stream per page region.

stream/1 is a normal Nova controller returning {stream, Code, Headers, Source}. That tuple is picked up by a return-handler we register -(handle_stream/3), which stream_replys the SSE headers and then holds the -connection, pushing datastar:patch_elements/2 frames and never returning - -so Nova's buffered render_response (which would reply again and crash) never -runs. On client disconnect the next stream_body fails and the request process -exits, dropping any tracer subscription. See novaframework/nova#387.

Two stream shapes:

  • polled (vitals/overview/processes/ets/applications/ports/supervisors/ -metrics): a receive ... after RefreshMs timer repaints the region. The -metrics stream threads the nova_liveboard_data:collect_metrics/1 -accumulator through its loop so sparklines and scheduler deltas build up.
  • event-driven (requests): subscribes to nova_liveboard_tracer and -prepends each request as it completes; no polling.

The handler is registered as an explicit fun/3 because current Nova invokes -return-handlers with 3 args, while its {Mod, Fun} form wraps to arity 4.

-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- -
- - -
- -
-
- register() - -
- -
- -
-
- stream(Req) - -
- -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

handle_stream/3

- - -
-
- -
- - -
-
-
- -
- - - -
-

register()

- - -
-
- -
- -
- -
-spec register() -> ok | {error, atom()}.
- -
- - -
-
-
- -
- - - -
-

stream(Req)

- - -
-
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_sup.html b/doc/nova_liveboard_sup.html deleted file mode 100644 index 8cccb3c..0000000 --- a/doc/nova_liveboard_sup.html +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - - - - nova_liveboard_sup — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_sup - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

Top supervisor: owns the request tracer.

-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- -
-
- init/1 - -
- -
- -
-
- start_link() - -
- -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

init/1

- - -
-
- -
- - -
-
-
- -
- - - -
-

start_link()

- - -
-
- -
- -
- -
-spec start_link() -> {ok, pid()}.
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_trace_plugin.html b/doc/nova_liveboard_trace_plugin.html deleted file mode 100644 index 2520529..0000000 --- a/doc/nova_liveboard_trace_plugin.html +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - - - - - - nova_liveboard_trace_plugin — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_trace_plugin - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

Nova plugin that feeds the liveboard's request tracer.

Register it as a global plugin in your Nova config to capture every request -handled by the node:

{nova, [
-  {plugins, [
-    {pre_request,  nova_liveboard_trace_plugin, #{}},
-    {post_request, nova_liveboard_trace_plugin, #{}}
-  ]}
-]}.

pre_request/4 stamps the request with an id, start time, reductions baseline -and the resolved handler MFA (from Env#{callback}), and - when deep trace is -armed - turns on scoped process tracing of the handling process. -post_request/4 computes duration / reductions / peak memory / status and -hands the summary to nova_liveboard_tracer. The whole chain runs in one -process, so the marker stashed in Req survives from pre to post.

The dashboard's own routes are skipped so it never traces itself (its SSE -streams never return, and would otherwise never finalise).

-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- -
-
- init() - -
- -
- -
- - -
- - - - - -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

init()

- - -
-
- -
- -
- -
-spec init() -> {ok, undefined}.
- -
- - -
-
-
- -
- - - -
-

plugin_info()

- - -
-
- -
- - -
-
-
- -
- - - -
-

post_request(Req, Env, Opts, State)

- - -
-
- -
- - -
-
-
- -
- - - -
-

pre_request(Req, Env, Opts, State)

- - -
-
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/nova_liveboard_tracer.html b/doc/nova_liveboard_tracer.html deleted file mode 100644 index e2b4b6d..0000000 --- a/doc/nova_liveboard_tracer.html +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - - - - - - nova_liveboard_tracer — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - -
-
-

- nova_liveboard_tracer - (nova_liveboard v0.0.0+build.11.ref1019ff1) - -

- - - - -
- - -
-

Request store and deep-trace collector for the liveboard.

Two jobs in one gen_server:

  1. Store - a capped, newest-first ring of recently completed requests -(nova_liveboard:request_buffer/0 deep). nova_liveboard_trace_plugin -calls finalize/2 from each request's post_request hook; SSE feeds -subscribe/0 to it for live updates.

  2. Trace collector - when deep trace is armed (start_trace/1), the next -N requests have their handling process traced with -erlang:trace(self(), true, [procs, set_on_spawn, {tracer, Pid}]) (the -plugin enables it; this server is the Pid). It receives -{trace, Parent, spawn, Child, MFA} messages, attributes each spawned -process to the owning request by walking the parent chain, and attaches the -resulting spawned-process tree to the request record on finalize/2.

The dashboard works without any of this; only the Requests page needs the -plugin registered.

-
- -
- -
-

- - - - Summary -

-
-

- Functions -

- -
-
- active() - -
- -

Whether any request has been captured (a proxy for 'plugin is wired up').

- -
- -
-
- clear() - -
- -
- -
- - -

Record a completed request; attaches the spawned tree if it was traced.

- -
- -
-
- get(ReqId) - -
- -
- -
- - -
- -
- - -
- -
- - -
- -
-
- init/1 - -
- -
- -
- - -

Ask whether the request ReqId handled by RootPid should be deep-traced.

- -
- -
-
- recent() - -
- -
- -
-
- start_link() - -
- -
- -
- - -
- -
-
- stop_trace() - -
- -
- -
-
- subscribe() - -
- -

Register the caller for live request + trace-state notifications.

- -
- -
- - -
- -
- -
- - -
-

- - - - Functions -

- -
-
- -
- - - -
-

active()

- - -
-
- -
- -
- -
-spec active() -> boolean().
- -
- -

Whether any request has been captured (a proxy for 'plugin is wired up').

-
-
-
- -
- - - -
-

clear()

- - -
-
- -
- -
- -
-spec clear() -> ok.
- -
- - -
-
-
- -
- - - -
-

finalize(ReqId, Summary)

- - -
-
- -
- -
- -
-spec finalize(binary(), map()) -> ok.
- -
- -

Record a completed request; attaches the spawned tree if it was traced.

-
-
-
- -
- - - -
-

get(ReqId)

- - -
-
- -
- -
- -
-spec get(binary()) -> map() | undefined.
- -
- - -
-
-
- -
- - - -
-

handle_call/3

- - -
-
- -
- - -
-
-
- -
- - - -
-

handle_cast/2

- - -
-
- -
- - -
-
-
- -
- - - -
-

handle_info/2

- - -
-
- -
- - -
-
-
- -
- - - -
-

init/1

- - -
-
- -
- - -
-
-
- -
- - - -
-

maybe_arm(ReqId, RootPid)

- - -
-
- -
- -
- -
-spec maybe_arm(binary(), pid()) -> trace | no_trace.
- -
- -

Ask whether the request ReqId handled by RootPid should be deep-traced.

Returns trace (and registers the root pid) when deep trace is armed, else -no_trace. The caller (the request process) is responsible for calling -erlang:trace/3 on itself when trace is returned.

-
-
-
- -
- - - -
-

recent()

- - -
-
- -
- -
- -
-spec recent() -> [map()].
- -
- - -
-
-
- -
- - - -
-

start_link()

- - -
-
- -
- -
- -
-spec start_link() -> {ok, pid()}.
- -
- - -
-
-
- -
- - - -
-

start_trace(N)

- - -
-
- -
- -
- -
-spec start_trace(pos_integer()) -> ok.
- -
- - -
-
-
- -
- - - -
-

stop_trace()

- - -
-
- -
- -
- -
-spec stop_trace() -> ok.
- -
- - -
-
-
- -
- - - -
-

subscribe()

- - -
-
- -
- -
- -
-spec subscribe() -> ok.
- -
- -

Register the caller for live request + trace-state notifications.

-
-
-
- -
- - - -
-

trace_state()

- - -
-
- -
- -
- -
-spec trace_state() -> map().
- -
- - -
-
- -
-
- - -
-
-
- - - diff --git a/doc/search.html b/doc/search.html deleted file mode 100644 index 268540b..0000000 --- a/doc/search.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - - Search — nova_liveboard v0.0.0+build.11.ref1019ff1 - - - - - - - - - - - -
- - - - - - - -
-
- - - - - -
-
-
- - - diff --git a/rebar3.crashdump b/rebar3.crashdump deleted file mode 100644 index fa6b490..0000000 --- a/rebar3.crashdump +++ /dev/null @@ -1,25 +0,0 @@ -Error: badarg -[{io,format, - ["\e[0;35m===> ~s ~s (~s)~n\e[0m", - [[128994,32,76,79,87,32,32,32,32,32],<<"cowlib">>,<<"2.16.1">>]], - [{file,"io.erl"}, - {line,205}, - {error_info,#{cause => {device,format},module => erl_stdlib_errors}}]}, - {rebar3_audit_prv,report_vuln,1, - [{file,"/home/dnwid/ai/personal/nova_liveboard/_build/default/plugins/rebar3_audit/src/rebar3_audit_prv.erl"}, - {line,204}]}, - {lists,foreach_1,2,[{file,"lists.erl"},{line,2641}]}, - {rebar3_audit_prv,do,1, - [{file,"/home/dnwid/ai/personal/nova_liveboard/_build/default/plugins/rebar3_audit/src/rebar3_audit_prv.erl"}, - {line,54}]}, - {rebar_core,do,2, - [{file,"/home/runner/work/rebar3/rebar3/apps/rebar/src/rebar_core.erl"}, - {line,155}]}, - {rebar3,run_aux,2, - [{file,"/home/runner/work/rebar3/rebar3/apps/rebar/src/rebar3.erl"}, - {line,212}]}, - {rebar3,main,1, - [{file,"/home/runner/work/rebar3/rebar3/apps/rebar/src/rebar3.erl"}, - {line,66}]}, - {escript,run,2,[{file,"escript.erl"},{line,908}]}] - From 7aa6a751db11dd467ffec68098e7fef49d674453 Mon Sep 17 00:00:00 2001 From: Daniel Widgren Date: Thu, 28 May 2026 07:01:31 +0200 Subject: [PATCH 5/5] ci: use correct erlang-ci v2.1.1 input names, pin OTP 29 --- .github/workflows/ci.yml | 14 ++++++-------- rebar.config | 3 +-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b516502..4444cdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,11 +18,9 @@ jobs: ci: uses: Taure/erlang-ci/.github/workflows/ci.yml@v2.1.1 with: - # audit + sbom are temporarily off: with this repo's `{vsn, git}` deps, - # rebar3_sbom badarg's on the `git` atom version and rebar3_audit crashes - # formatting a LOW-advisory glyph under OTP 29. Re-enable once fixed. - audit: false - sbom: false - sbom-scan: false - dep-submission: false - summary: true + otp-version: '29' + enable-ex-doc: true + # fmt/xref/dialyzer/eunit/summary are on by default. audit + sbom are left + # off (their defaults): with this repo's `{vsn, git}` deps, rebar3_sbom + # badarg's on the `git` atom version and rebar3_audit crashes formatting a + # LOW-advisory glyph under OTP 29. Re-enable once the plugins handle it. diff --git a/rebar.config b/rebar.config index 1c909f7..fb88e18 100644 --- a/rebar.config +++ b/rebar.config @@ -12,8 +12,7 @@ ]}. {project_plugins, [ - {erlfmt, "~>1.7"}, - covertool + {erlfmt, "~>1.7"} ]}. {erlfmt, [