Current section
Files
Jump to
Current section
Files
lib/live_debugger/app/debugger/web/debugger_live.ex
defmodule LiveDebugger.App.Debugger.Web.DebuggerLive do
@moduledoc """
Main page of the LiveDebugger.
It contains many components to debug the LiveView process.
When DeadViewMode is enabled it will also allow to debug process even after the LiveView is dead.
"""
use LiveDebugger.App.Web, :live_view
alias LiveDebugger.App.Debugger.Web.Hooks
alias LiveDebugger.App.Debugger.Web.HookComponents
alias LiveDebugger.App.Debugger.Web.Components.Pages
alias LiveDebugger.App.Web.Components.Navbar
alias LiveDebugger.App.Web.Helpers.Routes, as: RoutesHelper
alias LiveDebugger.App.Utils.Parsers
alias LiveDebugger.App.Debugger.Structs.TreeNode
alias LiveDebugger.Bus
alias LiveDebugger.App.Debugger.Events.NodeIdParamChanged
alias LiveDebugger.App.Debugger.Web.Components.NavigationMenu
alias LiveDebugger.App.Web.LiveComponents.TracerStatus
alias LiveDebugger.App.Web.Hooks.TracerStatus, as: TracerStatusHook
alias LiveDebugger.App.Utils.URL
@views_with_sidebar [
"node_inspector",
"global_traces"
]
@impl true
def mount(%{"pid" => string_pid}, _session, socket) do
if connected?(socket) do
Bus.receive_events!()
end
string_pid
|> Parsers.string_to_pid()
|> case do
{:ok, pid} ->
init_debugger(socket, pid)
:error ->
push_navigate(socket, to: RoutesHelper.error("invalid_pid"))
end
|> ok()
end
@impl true
def handle_params(params, _url, socket) do
socket
|> assign_and_broadcast_node_id(params)
|> handle_from(params)
|> noreply()
end
@impl true
def render(assigns) do
# remove the 'from' query parameter from the URL to avoid opening sidebar after returning to this page
assigns = assign(assigns, :url, URL.remove_query_param(assigns.url, "from"))
assigns =
assign(assigns,
show_sidebar_icon?: NavigationMenu.get_current_view(assigns.url) in @views_with_sidebar
)
~H"""
<div id="lv-process-live" class="w-screen h-screen grid grid-rows-[auto_1fr]">
<.async_result :let={lv_process} assign={@lv_process}>
<:loading>
<div class="flex h-screen items-center justify-center">
<.spinner size="xl" />
</div>
</:loading>
<div>
<.live_component
module={TracerStatus}
id="tracer-status"
tracer_started?={@tracer_started?}
/>
<Navbar.navbar class="grid grid-cols-[auto_auto_auto_1fr_auto] pl-2 lg:pr-4">
<Navbar.return_link return_link={RoutesHelper.discovery()} />
<Navbar.live_debugger_logo_icon />
<HookComponents.DeadViewMode.render id="navbar-connected" lv_process={lv_process} />
<span />
<div class="flex items-center gap-2">
<Navbar.garbage_collection_warning />
<Navbar.settings_button return_to={@url} />
<span :if={@show_sidebar_icon?} class="h-5 border-r border-default-border md_ct:hidden">
</span>
<.nav_icon
:if={@show_sidebar_icon?}
phx-click={if @lv_process.ok?, do: Pages.get_open_sidebar_js(@live_action)}
class="flex md_ct:hidden"
icon="icon-panel-right"
/>
</div>
</Navbar.navbar>
</div>
<div class="flex overflow-auto w-full">
<Pages.node_inspector
:if={@live_action == :node_inspector}
socket={@socket}
lv_process={lv_process}
url={@url}
node_id={@node_id}
inspect_mode?={@inspect_mode?}
return_link={RoutesHelper.discovery()}
trigger_sidebar={@trigger_sidebar}
/>
<Pages.global_traces
:if={@live_action == :global_traces}
socket={@socket}
lv_process={lv_process}
url={@url}
inspect_mode?={@inspect_mode?}
return_link={RoutesHelper.discovery()}
/>
<Pages.resources
:if={@live_action == :resources}
socket={@socket}
lv_process={lv_process}
url={@url}
inspect_mode?={@inspect_mode?}
return_link={RoutesHelper.discovery()}
/>
</div>
</.async_result>
</div>
"""
end
@impl true
def handle_info(_, socket), do: {:noreply, socket}
defp init_debugger(socket, pid) when is_pid(pid) do
socket
|> Hooks.AsyncLvProcess.init(pid)
|> put_private(:pid, pid)
|> HookComponents.DeadViewMode.init()
|> HookComponents.InspectButton.init()
|> TracerStatusHook.init()
end
defp assign_and_broadcast_node_id(socket, %{"node_id" => node_id}) do
socket_node_id = socket.assigns[:node_id]
node_id
|> TreeNode.id_from_string()
|> case do
{:ok, ^socket_node_id} ->
Pages.close_node_inspector_sidebar()
socket
{:ok, node_id} ->
Bus.broadcast_event!(%NodeIdParamChanged{node_id: node_id, debugger_pid: self()}, self())
Pages.close_node_inspector_sidebar()
assign(socket, :node_id, node_id)
:error ->
push_navigate(socket, to: RoutesHelper.error("invalid_node_id"))
end
end
defp assign_and_broadcast_node_id(socket, _) do
assign(socket, :node_id, socket.private.pid)
end
defp handle_from(socket, %{"from" => "inspect_button"}) do
assign(socket, :trigger_sidebar, true)
end
defp handle_from(socket, _params) do
assign(socket, :trigger_sidebar, false)
end
end