-module(spectator@internal@components@processes_live). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/spectator/internal/components/processes_live.gleam"). -export([app/0]). -export_type([model/0, msg/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(false). -type model() :: {model, gleam@option:option(gleam@erlang@atom:atom_()), list({binary(), binary()}), gleam@option:option(gleam@erlang@process:subject(msg())), integer(), list(spectator@internal@api:process_item()), spectator@internal@api:process_sort_criteria(), spectator@internal@api:sort_direction(), gleam@option:option(spectator@internal@api:process_item()), gleam@option:option(spectator@internal@api:process_details()), gleam@option:option(spectator@internal@api:process_otp_status()), gleam@option:option(gleam@dynamic:dynamic_())}. -opaque msg() :: refresh | {received_otp_details, spectator@internal@api:otp_details()} | {created_subject, gleam@erlang@process:subject(msg())} | {process_clicked, spectator@internal@api:process_item()} | {heading_clicked, spectator@internal@api:process_sort_criteria()} | {otp_state_clicked, spectator@internal@api:process_item(), spectator@internal@api:sys_state()} | {pid_clicked, gleam@erlang@process:pid_()} | {kill_clicked, spectator@internal@api:process_item()}. -file("src/spectator/internal/components/processes_live.gleam", 42). ?DOC(false). -spec emit_message(msg()) -> lustre@effect:effect(msg()). emit_message(Msg) -> lustre@effect:from(fun(Dispatch) -> Dispatch(Msg) end). -file("src/spectator/internal/components/processes_live.gleam", 46). ?DOC(false). -spec request_otp_details( gleam@option:option(gleam@erlang@atom:atom_()), gleam@erlang@process:pid_() ) -> lustre@effect:effect(msg()). request_otp_details(Node, Pid) -> lustre@effect:from( fun(Dispatch) -> spectator@internal@api:request_otp_data( Node, Pid, fun(Details) -> Dispatch({received_otp_details, Details}) end ), nil end ). -file("src/spectator/internal/components/processes_live.gleam", 59). ?DOC(false). -spec init(list({binary(), binary()})) -> {model(), lustre@effect:effect(msg())}. init(Params) -> Node = spectator@internal@api:node_from_params(Params), Default_sort_criteria = sort_by_reductions, Default_sort_direction = descending, Refresh_interval = spectator@internal@common:get_refresh_interval(Params), {{model, Node, spectator@internal@common:sanitize_params(Params), none, Refresh_interval, [], Default_sort_criteria, Default_sort_direction, none, none, none, none}, lustre@effect:batch( [emit_message(refresh), case spectator@internal@common:get_param( Params, <<"selected"/utf8>> ) of {error, _} -> lustre@effect:none(); {ok, Potential_pid} -> case spectator@internal@api:decode_pid(Potential_pid) of {ok, Pid} -> emit_message({pid_clicked, Pid}); {error, _} -> lustre@effect:none() end end] )}. -file("src/spectator/internal/components/processes_live.gleam", 110). ?DOC(false). -spec do_refresh(model()) -> model(). do_refresh(Model) -> Info = spectator@internal@api:get_process_list(erlang:element(2, Model)), Sorted = spectator@internal@api:sort_process_list( Info, erlang:element(7, Model), erlang:element(8, Model) ), Active_process@1 = case erlang:element(9, Model) of none -> none; {some, Active_process} -> case spectator_ffi:get_process_info( erlang:element(2, Model), erlang:element(2, Active_process) ) of {ok, Info@1} -> {some, {process_item, erlang:element(2, Active_process), Info@1}}; {error, _} -> none end end, Details@1 = case Active_process@1 of none -> none; {some, Ap} -> case spectator_ffi:get_details( erlang:element(2, Model), erlang:element(2, Ap) ) of {ok, Details} -> {some, Details}; {error, _} -> none end end, {model, erlang:element(2, Model), erlang:element(3, Model), erlang:element(4, Model), erlang:element(5, Model), Sorted, erlang:element(7, Model), erlang:element(8, Model), Active_process@1, Details@1, erlang:element(11, Model), erlang:element(12, Model)}. -file("src/spectator/internal/components/processes_live.gleam", 139). ?DOC(false). -spec update(model(), msg()) -> {model(), lustre@effect:effect(msg())}. update(Model, Msg) -> case Msg of refresh -> New_model = do_refresh(Model), case erlang:element(9, New_model) of {some, P} when erlang:element(6, erlang:element(3, P)) < 10 -> {New_model, lustre@effect:batch( [request_otp_details( erlang:element(2, Model), erlang:element(2, P) ), spectator@internal@common:emit_after( erlang:element(5, Model), refresh, erlang:element(4, Model), fun(Field@0) -> {created_subject, Field@0} end )] )}; _ -> {New_model, spectator@internal@common:emit_after( erlang:element(5, Model), refresh, erlang:element(4, Model), fun(Field@0) -> {created_subject, Field@0} end )} end; {created_subject, Subject} -> {{model, erlang:element(2, Model), erlang:element(3, Model), {some, Subject}, erlang:element(5, Model), erlang:element(6, Model), erlang:element(7, Model), erlang:element(8, Model), erlang:element(9, Model), erlang:element(10, Model), erlang:element(11, Model), erlang:element(12, Model)}, lustre@effect:none()}; {process_clicked, P@1} -> New_model@1 = begin _pipe = {model, erlang:element(2, Model), erlang:element(3, Model), erlang:element(4, Model), erlang:element(5, Model), erlang:element(6, Model), erlang:element(7, Model), erlang:element(8, Model), {some, P@1}, erlang:element(10, Model), none, none}, do_refresh(_pipe) end, {New_model@1, request_otp_details( erlang:element(2, Model), erlang:element(2, P@1) )}; {heading_clicked, Criteria} -> case Criteria of C when C =:= erlang:element(7, Model) -> New_model@2 = {model, erlang:element(2, Model), erlang:element(3, Model), erlang:element(4, Model), erlang:element(5, Model), erlang:element(6, Model), erlang:element(7, Model), spectator@internal@api:invert_sort_direction( erlang:element(8, Model) ), erlang:element(9, Model), erlang:element(10, Model), erlang:element(11, Model), erlang:element(12, Model)}, {do_refresh(New_model@2), lustre@effect:none()}; C@1 -> New_model@3 = {model, erlang:element(2, Model), erlang:element(3, Model), erlang:element(4, Model), erlang:element(5, Model), erlang:element(6, Model), C@1, erlang:element(8, Model), erlang:element(9, Model), erlang:element(10, Model), erlang:element(11, Model), erlang:element(12, Model)}, {do_refresh(New_model@3), lustre@effect:none()} end; {received_otp_details, Details} -> case erlang:element(9, Model) of {some, P@2} when erlang:element(2, P@2) =:= erlang:element( 2, Details ) -> {{model, erlang:element(2, Model), erlang:element(3, Model), erlang:element(4, Model), erlang:element(5, Model), erlang:element(6, Model), erlang:element(7, Model), erlang:element(8, Model), erlang:element(9, Model), erlang:element(10, Model), {some, erlang:element(3, Details)}, {some, erlang:element(4, Details)}}, lustre@effect:none()}; _ -> {Model, lustre@effect:none()} end; {otp_state_clicked, P@3, Target_sys_state} -> case Target_sys_state of process_suspended -> _ = spectator_ffi:sys_resume( erlang:element(2, Model), erlang:element(2, P@3) ), {do_refresh(Model), request_otp_details( erlang:element(2, Model), erlang:element(2, P@3) )}; process_running -> _ = spectator_ffi:sys_suspend( erlang:element(2, Model), erlang:element(2, P@3) ), {do_refresh(Model), request_otp_details( erlang:element(2, Model), erlang:element(2, P@3) )} end; {pid_clicked, Pid} -> case spectator_ffi:get_process_info(erlang:element(2, Model), Pid) of {ok, Info} -> P@4 = {process_item, Pid, Info}, New_model@4 = begin _pipe@1 = {model, erlang:element(2, Model), erlang:element(3, Model), erlang:element(4, Model), erlang:element(5, Model), erlang:element(6, Model), erlang:element(7, Model), erlang:element(8, Model), {some, P@4}, erlang:element(10, Model), none, none}, do_refresh(_pipe@1) end, {New_model@4, request_otp_details( erlang:element(2, Model), erlang:element(2, P@4) )}; {error, _} -> {Model, lustre@effect:none()} end; {kill_clicked, P@5} -> _ = spectator_ffi:kill_process( erlang:element(2, Model), erlang:element(2, P@5) ), {do_refresh(Model), lustre@effect:none()} end. -file("src/spectator/internal/components/processes_live.gleam", 354). ?DOC(false). -spec render_name(spectator@internal@api:process_item()) -> lustre@vdom@vnode:element(any()). render_name(Process) -> case erlang:element(4, erlang:element(3, Process)) of none -> spectator@internal@views@display:pid(erlang:element(2, Process)); {some, Name} -> lustre@element@html:text(erlang:atom_to_binary(Name)) end. -file("src/spectator/internal/components/processes_live.gleam", 361). ?DOC(false). -spec render_tag(spectator@internal@api:process_item()) -> lustre@vdom@vnode:element(any()). render_tag(Process) -> case erlang:element(8, erlang:element(3, Process)) of none -> spectator@internal@views@display:function( erlang:element(3, erlang:element(3, Process)) ); {some, <<"__spectator_internal "/utf8, Rest/binary>>} -> lustre@element@html:text(<<"🔍 "/utf8, Rest/binary>>); {some, Tag} -> lustre@element@html:text(<<"🔖 "/utf8, Tag/binary>>) end. -file("src/spectator/internal/components/processes_live.gleam", 369). ?DOC(false). -spec render_primitive_list( list(spectator@internal@api:system_primitive()), fun((gleam@erlang@process:pid_()) -> msg()), list({binary(), binary()}) ) -> list(lustre@vdom@vnode:element(msg())). render_primitive_list(Primitives, On_primitive_click, Params) -> _pipe = gleam@list:map( Primitives, fun(_capture) -> spectator@internal@views@display:system_primitive_interactive( _capture, On_primitive_click, Params ) end ), gleam@list:intersperse(_pipe, lustre@element@html:text(<<", "/utf8>>)). -file("src/spectator/internal/components/processes_live.gleam", 382). ?DOC(false). -spec render_details( gleam@option:option(spectator@internal@api:process_item()), gleam@option:option(spectator@internal@api:process_details()), gleam@option:option(spectator@internal@api:process_otp_status()), gleam@option:option(gleam@dynamic:dynamic_()), fun((spectator@internal@api:process_item(), spectator@internal@api:sys_state()) -> msg()), list({binary(), binary()}) ) -> lustre@vdom@vnode:element(msg()). render_details(P, D, Status, State, Handle_otp_state_click, Params) -> case {P, D} of {{some, Proc}, {some, Details}} -> lustre@element@html:'div'( [lustre@attribute:class(<<"details"/utf8>>)], [lustre@element@html:'div'( [lustre@attribute:class(<<"general"/utf8>>)], [lustre@element@html:'div'( [lustre@attribute:class( <<"panel-heading"/utf8>> )], [case {erlang:element( 8, erlang:element(3, Proc) ), erlang:element( 4, erlang:element(3, Proc) )} of {{some, <<"__spectator_internal "/utf8, Rest/binary>>}, _} -> lustre@element@html:text( <<"🔍 Spectator "/utf8, Rest/binary>> ); {{some, Tag}, none} -> lustre@element@html:text( <<<<<<"🔖 "/utf8, (spectator_ffi:format_pid( erlang:element( 2, Proc ) ))/binary>>/binary, " "/utf8>>/binary, Tag/binary>> ); {{some, Tag@1}, {some, Name}} -> lustre@element@html:text( <<<<<<<<<<"🔖 "/utf8, (spectator_ffi:format_pid( erlang:element( 2, Proc ) ))/binary>>/binary, " "/utf8>>/binary, Tag@1/binary>>/binary, " registered as "/utf8>>/binary, (erlang:atom_to_binary(Name))/binary>> ); {none, none} -> lustre@element@html:text( <<<<"🎛️ "/utf8, (spectator_ffi:format_pid( erlang:element( 2, Proc ) ))/binary>>/binary, " Process"/utf8>> ); {none, {some, Name@1}} -> lustre@element@html:text( <<<<<<"📠 "/utf8, (spectator_ffi:format_pid( erlang:element( 2, Proc ) ))/binary>>/binary, " "/utf8>>/binary, (erlang:atom_to_binary( Name@1 ))/binary>> ) end, lustre@element@html:button( [lustre@attribute:class( <<"panel-action suspend"/utf8>> ), lustre@event:on_click( {kill_clicked, Proc} )], [lustre@element@html:text( <<"🗡️ Kill"/utf8>> )] )] ), lustre@element@html:'div'( [lustre@attribute:class( <<"panel-content"/utf8>> )], [lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Links"/utf8>> )] ), lustre@element@html:dd( [], render_primitive_list( erlang:element(3, Details), fun(Field@0) -> {pid_clicked, Field@0} end, Params ) )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Monitored By"/utf8>> )] ), lustre@element@html:dd( [], render_primitive_list( erlang:element(4, Details), fun(Field@0) -> {pid_clicked, Field@0} end, Params ) )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Monitors"/utf8>> )] ), lustre@element@html:dd( [], render_primitive_list( erlang:element(5, Details), fun(Field@0) -> {pid_clicked, Field@0} end, Params ) )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Parent"/utf8>> )] ), lustre@element@html:dd( [], [case erlang:element(7, Details) of none -> lustre@element@html:text( <<"None"/utf8>> ); {some, Parent} -> spectator@internal@views@display:system_primitive_interactive( Parent, fun(Field@0) -> {pid_clicked, Field@0} end, Params ) end] )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Status"/utf8>> )] ), lustre@element@html:dd( [], [lustre@element@html:text( erlang:atom_to_binary( erlang:element( 9, erlang:element( 3, Proc ) ) ) )] )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Trap Exit"/utf8>> )] ), lustre@element@html:dd( [], [spectator@internal@views@display:bool( erlang:element( 6, Details ) )] )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Initial Call"/utf8>> )] ), lustre@element@html:dd( [], [spectator@internal@views@display:function( erlang:element( 3, erlang:element( 3, Proc ) ) )] )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Current Function"/utf8>> )] ), lustre@element@html:dd( [], [spectator@internal@views@display:function( erlang:element( 2, erlang:element( 3, Proc ) ) )] )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Reductions"/utf8>> )] ), lustre@element@html:dd( [], [spectator@internal@views@display:number( erlang:element( 7, erlang:element( 3, Proc ) ) )] )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Memory"/utf8>> )] ), lustre@element@html:dd( [], [spectator@internal@views@display:storage_detailed( erlang:element( 5, erlang:element( 3, Proc ) ) )] )] ), lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Message Queue Length"/utf8>> )] ), lustre@element@html:dd( [], [spectator@internal@views@display:number( erlang:element( 6, erlang:element( 3, Proc ) ) )] )] )] )] ), lustre@element@html:'div'( [lustre@attribute:class(<<"otp"/utf8>>)], case {Status, State} of {{some, Status@1}, {some, State@1}} -> [lustre@element@html:'div'( [lustre@attribute:class( <<"panel-heading"/utf8>> )], [lustre@element@html:strong( [], [lustre@element@html:text( <<"☎️ OTP Process: "/utf8>> )] ), spectator@internal@views@display:atom( erlang:element(3, Status@1) ), case erlang:element(5, Status@1) of process_suspended -> lustre@element@html:button( [lustre@event:on_click( Handle_otp_state_click( Proc, erlang:element( 5, Status@1 ) ) ), lustre@attribute:class( <<"panel-action resume"/utf8>> )], [lustre@element@html:text( <<"🏃‍♀️‍➡️ Resume"/utf8>> )] ); process_running -> lustre@element@html:button( [lustre@event:on_click( Handle_otp_state_click( Proc, erlang:element( 5, Status@1 ) ) ), lustre@attribute:class( <<"panel-action suspend"/utf8>> )], [lustre@element@html:text( <<"✋ Suspend"/utf8>> )] ) end] ), lustre@element@html:'div'( [lustre@attribute:class( <<"panel-content"/utf8>> )], [lustre@element@html:pre( [], [lustre@element@html:text( pprint:format(State@1) )] )] )]; {_, _} when erlang:element( 6, erlang:element(3, Proc) ) >= 10 -> [lustre@element@html:'div'( [lustre@attribute:class( <<"panel-content"/utf8>> )], [lustre@element@html:strong( [], [lustre@element@html:text( <<"⚠️ Spectator has stopped trying to inspect the OTP state of this process"/utf8>> )] ), lustre@element@html:p( [], [lustre@element@html:text( <<"The message queue length is above the threshold of "/utf8>> ), spectator@internal@views@display:number( 10 ), lustre@element@html:text( <<"."/utf8>> ), lustre@element@html:br([]), lustre@element@html:text( <<"Spectator will not send it any more system messages to avoid filling the message queue."/utf8>> )] )] )]; {_, _} -> [lustre@element@html:'div'( [lustre@attribute:class( <<"panel-content"/utf8>> )], [lustre@element@html:text( <<"This process does not appear to be OTP-compliant"/utf8>> )] )] end )] ); {_, _} -> lustre@element@html:'div'( [lustre@attribute:class(<<"footer-placeholder"/utf8>>)], [lustre@element@html:text( <<"Click a process to see details"/utf8>> )] ) end. -file("src/spectator/internal/components/processes_live.gleam", 563). ?DOC(false). -spec classify_selected( spectator@internal@api:process_item(), gleam@option:option(spectator@internal@api:process_item()) ) -> lustre@vdom@vattr:attribute(any()). classify_selected(Process, Active) -> Selection_status = case Active of {some, Active@1} when erlang:element(2, Active@1) =:= erlang:element( 2, Process ) -> <<"selected"/utf8>>; _ -> <<""/utf8>> end, case erlang:element(8, erlang:element(3, Process)) of none -> lustre@attribute:class(Selection_status); {some, <<"__spectator_internal "/utf8, _/binary>>} -> lustre@attribute:class( <> ); {some, _} -> lustre@attribute:class(<>) end. -file("src/spectator/internal/components/processes_live.gleam", 241). ?DOC(false). -spec view(model()) -> lustre@vdom@vnode:element(msg()). view(Model) -> lustre@element@html:table( [], [lustre@element@html:thead( [], [lustre@element@html:tr( [], [spectator@internal@views@table:heading( <<"Name"/utf8>>, <<"Process PID or registered name"/utf8>>, sort_by_process_name, erlang:element(7, Model), erlang:element(8, Model), fun(Field@0) -> {heading_clicked, Field@0} end, false ), spectator@internal@views@table:heading( <<"Tag"/utf8>>, <<"Spectator tag or initial call"/utf8>>, sort_by_tag, erlang:element(7, Model), erlang:element(8, Model), fun(Field@0) -> {heading_clicked, Field@0} end, false ), spectator@internal@views@table:heading( <<"Current"/utf8>>, <<"Current function"/utf8>>, sort_by_current_function, erlang:element(7, Model), erlang:element(8, Model), fun(Field@0) -> {heading_clicked, Field@0} end, false ), spectator@internal@views@table:heading( <<"Reductions"/utf8>>, <<"Number of reductions"/utf8>>, sort_by_reductions, erlang:element(7, Model), erlang:element(8, Model), fun(Field@0) -> {heading_clicked, Field@0} end, false ), spectator@internal@views@table:heading( <<"Memory"/utf8>>, <<"Memory usage"/utf8>>, sort_by_process_memory, erlang:element(7, Model), erlang:element(8, Model), fun(Field@0) -> {heading_clicked, Field@0} end, true ), spectator@internal@views@table:heading( <<"Msgs"/utf8>>, <<"Message queue size"/utf8>>, sort_by_message_queue, erlang:element(7, Model), erlang:element(8, Model), fun(Field@0) -> {heading_clicked, Field@0} end, true ), spectator@internal@views@table:heading( <<"Status"/utf8>>, <<"Process Status"/utf8>>, sort_by_process_status, erlang:element(7, Model), erlang:element(8, Model), fun(Field@0) -> {heading_clicked, Field@0} end, true )] )] ), lustre@element@html:tbody( [], spectator@internal@views@table:map_rows( erlang:element(6, Model), fun(Process) -> lustre@element@html:tr( [lustre@attribute:role(<<"button"/utf8>>), classify_selected( Process, erlang:element(9, Model) ), lustre@event:on_click( {process_clicked, Process} )], [lustre@element@html:td([], [render_name(Process)]), lustre@element@html:td( [], [render_tag(Process)] ), lustre@element@html:td( [], [spectator@internal@views@display:function( erlang:element( 2, erlang:element(3, Process) ) )] ), lustre@element@html:td( [], [spectator@internal@views@display:number( erlang:element( 7, erlang:element(3, Process) ) )] ), lustre@element@html:td( [lustre@attribute:class( <<"cell-right"/utf8>> )], [spectator@internal@views@display:storage( erlang:element( 5, erlang:element(3, Process) ) )] ), lustre@element@html:td( [lustre@attribute:class( <<"cell-right"/utf8>> )], [spectator@internal@views@display:number( erlang:element( 6, erlang:element(3, Process) ) )] ), lustre@element@html:td( [lustre@attribute:class( <<"cell-right"/utf8>> )], [spectator@internal@views@display:atom( erlang:element( 9, erlang:element(3, Process) ) )] )] ) end ) ), lustre@element@html:tfoot( [], [lustre@element@html:tr( [], [lustre@element@html:td( [lustre@attribute:attribute( <<"colspan"/utf8>>, <<"7"/utf8>> )], [render_details( erlang:element(9, Model), erlang:element(10, Model), erlang:element(11, Model), erlang:element(12, Model), fun(Field@0, Field@1) -> {otp_state_clicked, Field@0, Field@1} end, erlang:element(3, Model) )] )] )] )] ). -file("src/spectator/internal/components/processes_live.gleam", 20). ?DOC(false). -spec app() -> lustre@runtime@app:app(list({binary(), binary()}), model(), msg()). app() -> lustre:application(fun init/1, fun update/2, fun view/1).