-module(spectator@internal@components@processes_live). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([render/10, app/0]). -export_type([model/0, msg/0]). -type model() :: {model, gleam@option:option(gleam@erlang@process:subject(msg())), list(spectator@internal@api:process_item()), spectator@internal@api:info_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: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:info_sort_criteria()} | {otp_state_clicked, spectator@internal@api:process_item(), spectator@internal@api:sys_state()} | {system_primitive_clicked, spectator@internal@api:system_primitive()}. -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 41). -spec emit_after( integer(), msg(), gleam@option:option(gleam@erlang@process:subject(msg())) ) -> lustre@effect:effect(msg()). emit_after(Delay, Msg, Subject) -> case Subject of {some, Self} -> lustre@effect:from( fun(_) -> _ = gleam@erlang@process:send_after(Self, Delay, Msg), nil end ); none -> lustre@server_component:select( fun(Dispatch, Subject@1) -> Selector = begin _pipe = gleam_erlang_ffi:new_selector(), gleam@erlang@process:selecting( _pipe, Subject@1, fun(Msg@1) -> Msg@1 end ) end, _ = gleam@erlang@process:send_after(Subject@1, Delay, Msg), Dispatch({created_subject, Subject@1}), Selector end ) end. -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 63). -spec request_otp_details( gleam@erlang@process:pid_(), gleam@option:option(gleam@erlang@process:subject(msg())) ) -> lustre@effect:effect(msg()). request_otp_details(Pid, Subject) -> case Subject of {some, Sub} -> lustre@effect:from( fun(_) -> spectator@internal@api:request_otp_data( Pid, fun(Details) -> gleam@erlang@process:send( Sub, {received_otp_details, Details} ) end ), nil end ); none -> lustre@effect:none() end. -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 79). -spec init(any()) -> {model(), lustre@effect:effect(msg())}. init(_) -> Info = spectator@internal@api:get_info_list(), Default_sort_criteria = reductions, Default_sort_direction = descending, Sorted = spectator@internal@api:sort_info_list( Info, Default_sort_criteria, descending ), {{model, none, Sorted, Default_sort_criteria, Default_sort_direction, none, none, none, none}, emit_after(500, refresh, none)}. -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 111). -spec do_refresh(model()) -> model(). do_refresh(Model) -> Info = spectator@internal@api:get_info_list(), Sorted = spectator@internal@api:sort_info_list( Info, erlang:element(4, Model), erlang:element(5, Model) ), Active_process@1 = case erlang:element(6, Model) of none -> none; {some, Active_process} -> case spectator_ffi:get_info(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, Ap)) of {ok, Details} -> {some, Details}; {error, _} -> none end end, erlang:setelement( 7, erlang:setelement( 3, erlang:setelement(6, Model, Active_process@1), Sorted ), Details@1 ). -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 138). -spec update(model(), msg()) -> {model(), lustre@effect:effect(msg())}. update(Model, Msg) -> case Msg of refresh -> New_model = do_refresh(Model), case erlang:element(6, 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, P), erlang:element(2, Model) ), emit_after(500, refresh, none)] )}; _ -> {New_model, emit_after(500, refresh, none)} end; {created_subject, Subject} -> {erlang:setelement(2, Model, {some, Subject}), lustre@effect:none()}; {process_clicked, P@1} -> New_model@1 = begin _pipe = erlang:setelement( 8, erlang:setelement( 9, erlang:setelement(6, Model, {some, P@1}), none ), none ), do_refresh(_pipe) end, {New_model@1, request_otp_details( erlang:element(2, P@1), erlang:element(2, Model) )}; {heading_clicked, Criteria} -> case Criteria of C when C =:= erlang:element(4, Model) -> New_model@2 = erlang:setelement( 5, Model, spectator@internal@api:invert_sort_direction( erlang:element(5, Model) ) ), {do_refresh(New_model@2), lustre@effect:none()}; C@1 -> New_model@3 = erlang:setelement(4, Model, C@1), {do_refresh(New_model@3), lustre@effect:none()} end; {received_otp_details, Details} -> case erlang:element(6, Model) of {some, P@2} when erlang:element(2, P@2) =:= erlang:element( 2, Details ) -> {erlang:setelement( 8, erlang:setelement( 9, Model, {some, erlang:element(4, Details)} ), {some, erlang:element(3, Details)} ), lustre@effect:none()}; _ -> {Model, lustre@effect:none()} end; {otp_state_clicked, P@3, Target_sys_state} -> case Target_sys_state of suspended -> sys:resume(erlang:element(2, P@3)), {do_refresh(Model), request_otp_details( erlang:element(2, P@3), erlang:element(2, Model) )}; running -> sys:suspend(erlang:element(2, P@3)), {do_refresh(Model), request_otp_details( erlang:element(2, P@3), erlang:element(2, Model) )} end; {system_primitive_clicked, Primitive} -> case Primitive of {process, Pid} -> case spectator_ffi:get_info(Pid) of {ok, Info} -> P@4 = {process_item, Pid, Info}, New_model@4 = begin _pipe@1 = erlang:setelement( 8, erlang:setelement( 9, erlang:setelement(6, Model, {some, P@4}), none ), none ), do_refresh(_pipe@1) end, {New_model@4, request_otp_details( erlang:element(2, P@4), erlang:element(2, Model) )}; {error, _} -> {Model, lustre@effect:none()} end; _ -> logging:log( warning, <<"System primitive click unhandled"/utf8>> ), {Model, lustre@effect:none()} end end. -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 254). -spec render_name(spectator@internal@api:process_item()) -> lustre@internals@vdom: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("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 261). -spec render_tag(spectator@internal@api:process_item()) -> lustre@internals@vdom: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("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 269). -spec render_primitive_list( list(spectator@internal@api:system_primitive()), fun((spectator@internal@api:system_primitive()) -> msg()) ) -> list(lustre@internals@vdom:element(msg())). render_primitive_list(Primitives, On_primitive_click) -> _pipe = gleam@list:map( Primitives, fun(_capture) -> spectator@internal@views@display:system_primitive_interactive( _capture, On_primitive_click ) end ), gleam@list:intersperse(_pipe, lustre@element@html:text(<<", "/utf8>>)). -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 280). -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:status()), gleam@option:option(gleam@dynamic:dynamic_()), fun((spectator@internal@api:process_item(), spectator@internal@api:sys_state()) -> msg()) ) -> lustre@internals@vdom:element(msg()). render_details(P, D, Status, State, Handle_otp_state_click) -> 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)) of {some, <<"__spectator_internal "/utf8, Rest/binary>>} -> lustre@element@html:text( <<"🔍 Spectator "/utf8, Rest/binary>> ); {some, Tag} -> lustre@element@html:text( <<<<"🔖 "/utf8, Tag/binary>>/binary, " details"/utf8>> ); none -> lustre@element@html:text( <<"🎛️ Process Details"/utf8>> ) end] ), lustre@element@html:'div'( [lustre@attribute:class( <<"panel-content"/utf8>> )], [lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Process Id"/utf8>> )] ), lustre@element@html:dd( [], [spectator@internal@views@display:pid( erlang:element(2, Proc) )] )] ), case erlang:element( 8, erlang:element(3, Proc) ) of none -> lustre@element@html:text( <<""/utf8>> ); {some, Tag@1} -> lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Tag"/utf8>> )] ), lustre@element@html:dd( [], [lustre@element@html:text( Tag@1 )] )] ) end, case erlang:element( 4, erlang:element(3, Proc) ) of none -> lustre@element@html:text( <<""/utf8>> ); {some, Name} -> lustre@element@html:dl( [], [lustre@element@html:dt( [], [lustre@element@html:text( <<"Registered Name"/utf8>> )] ), lustre@element@html:dd( [], [lustre@element@html:text( erlang:atom_to_binary( Name ) )] )] ) 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( <<"Links"/utf8>> )] ), lustre@element@html:dd( [], render_primitive_list( erlang:element(3, Details), fun(Field@0) -> {system_primitive_clicked, Field@0} end ) )] ), 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) -> {system_primitive_clicked, Field@0} end ) )] ), 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) -> {system_primitive_clicked, Field@0} end ) )] ), 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, Pid} -> spectator@internal@views@display:pid_button( Pid, fun(Pid@1) -> {system_primitive_clicked, {process, Pid@1}} end ) end] )] ), 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 suspended -> lustre@element@html:button( [lustre@event:on_click( Handle_otp_state_click( Proc, erlang:element( 5, Status@1 ) ) ), lustre@attribute:class( <<"otp-toggle resume"/utf8>> )], [lustre@element@html:text( <<"🏃‍♀️‍➡️ Resume"/utf8>> )] ); running -> lustre@element@html:button( [lustre@event:on_click( Handle_otp_state_click( Proc, erlang:element( 5, Status@1 ) ) ), lustre@attribute:class( <<"otp-toggle 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(<<"otp-placeholder"/utf8>>)], [lustre@element@html:text( <<"Click a process to see details"/utf8>> )] ) end. -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 456). -spec classify_selected( spectator@internal@api:process_item(), gleam@option:option(spectator@internal@api:process_item()) ) -> lustre@internals@vdom: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("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 469). -spec render( list(spectator@internal@api:process_item()), fun((spectator@internal@api:process_item()) -> msg()), gleam@option:option(spectator@internal@api:process_item()), gleam@option:option(spectator@internal@api:process_details()), gleam@option:option(spectator@internal@api:status()), gleam@option:option(gleam@dynamic:dynamic_()), spectator@internal@api:info_sort_criteria(), spectator@internal@api:sort_direction(), fun((spectator@internal@api:info_sort_criteria()) -> msg()), fun((spectator@internal@api:process_item(), spectator@internal@api:sys_state()) -> msg()) ) -> lustre@internals@vdom:element(msg()). render( Processes, Handle_process_click, Active, Details, Status, State, Sort_criteria, Sort_direction, Handle_heading_click, Handle_otp_state_click ) -> 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>>, name, Sort_criteria, Sort_direction, Handle_heading_click, false ), spectator@internal@views@table:heading( <<"Tag"/utf8>>, <<"Spectator tag or initial call"/utf8>>, tag, Sort_criteria, Sort_direction, Handle_heading_click, false ), spectator@internal@views@table:heading( <<"Current"/utf8>>, <<"Current function"/utf8>>, current_function, Sort_criteria, Sort_direction, Handle_heading_click, false ), spectator@internal@views@table:heading( <<"Reductions"/utf8>>, <<"Number of reductions"/utf8>>, reductions, Sort_criteria, Sort_direction, Handle_heading_click, false ), spectator@internal@views@table:heading( <<"Memory"/utf8>>, <<"Memory usage"/utf8>>, memory, Sort_criteria, Sort_direction, Handle_heading_click, true ), spectator@internal@views@table:heading( <<"Msgs"/utf8>>, <<"Message queue size"/utf8>>, message_queue, Sort_criteria, Sort_direction, Handle_heading_click, true ), spectator@internal@views@table:heading( <<"Status"/utf8>>, <<"Process Status"/utf8>>, process_status, Sort_criteria, Sort_direction, Handle_heading_click, false )] )] ), lustre@element@html:tbody( [], spectator@internal@views@table:map_rows( Processes, fun(Process) -> lustre@element@html:tr( [lustre@attribute:role(<<"button"/utf8>>), classify_selected(Process, Active), lustre@event:on_click( Handle_process_click(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( [], [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( Active, Details, Status, State, Handle_otp_state_click )] )] )] )] ). -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 239). -spec view(model()) -> lustre@internals@vdom:element(msg()). view(Model) -> render( erlang:element(3, Model), fun(Field@0) -> {process_clicked, Field@0} end, erlang:element(6, Model), erlang:element(7, Model), erlang:element(8, Model), erlang:element(9, Model), erlang:element(4, Model), erlang:element(5, Model), fun(Field@0) -> {heading_clicked, Field@0} end, fun(Field@0, Field@1) -> {otp_state_clicked, Field@0, Field@1} end ). -file("/Users/jonas/Projects/spectator/src/spectator/internal/components/processes_live.gleam", 22). -spec app() -> lustre:app(any(), model(), msg()). app() -> lustre:application(fun init/1, fun update/2, fun view/1).