Packages
mob_dev
0.3.28
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.37
0.3.35
0.3.34
0.3.33
0.3.28
0.3.26
0.3.23
0.3.21
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Development tooling for the Mob mobile framework
Current section
Files
Jump to
Current section
Files
lib/mob_dev/server/layouts.ex
defmodule MobDev.Server.Layouts do
use Phoenix.Component
@spec root(map()) :: Phoenix.LiveView.Rendered.t()
def root(assigns) do
~H"""
<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="csrf-token" content={Phoenix.Controller.get_csrf_token()}/>
<title>Mob Dev</title>
<link rel="icon" href="data:,"/>
<script src="https://cdn.tailwindcss.com"></script>
<script src="/assets/phoenix/phoenix.min.js"></script>
<script src="/assets/plv/phoenix_live_view.js"></script>
<script>
const Hooks = {};
// Keeps the log panel pinned to bottom unless the user scrolls up.
Hooks.ScrollBottom = {
mounted() { this.atBottom = true; this.scrollToBottom(); },
beforeUpdate() {
const el = this.el;
this.atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 20;
},
updated() { if (this.atBottom) this.scrollToBottom(); },
scrollToBottom() { this.el.scrollTop = this.el.scrollHeight; }
};
// Draggable divider between device logs and Elixir logs.
// Dragging left widens the Elixir pane; dragging right narrows it.
Hooks.ResizableDivider = {
mounted() {
const elixirPane = this.el.nextElementSibling;
const container = this.el.parentElement;
const minWidth = 180;
this.onMouseDown = (e) => {
e.preventDefault();
const startX = e.clientX;
const startWidth = elixirPane.offsetWidth;
const onMouseMove = (e) => {
const delta = startX - e.clientX;
const maxWidth = container.offsetWidth - 300;
const newWidth = Math.max(minWidth, Math.min(startWidth + delta, maxWidth));
elixirPane.style.width = newWidth + 'px';
elixirPane.style.flexShrink = '0';
elixirPane.style.flexBasis = newWidth + 'px';
};
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
};
this.el.addEventListener('mousedown', this.onMouseDown);
},
destroyed() {
this.el.removeEventListener('mousedown', this.onMouseDown);
}
};
let liveSocket = new LiveView.LiveSocket("/live", Phoenix.Socket, {
hooks: Hooks,
params: { _csrf_token: document.querySelector("meta[name='csrf-token']").getAttribute("content") }
});
liveSocket.connect();
</script>
<style>
.log-line { font-family: ui-monospace, monospace; font-size: 0.75rem; }
.log-E { color: #f87171; }
.log-W { color: #fbbf24; }
.log-I { color: #86efac; }
.log-D { color: #94a3b8; }
.log-mob { color: #a78bfa; font-weight: 600; }
#log-container { scroll-behavior: smooth; }
</style>
</head>
<body class="h-full bg-zinc-950 text-zinc-200 flex flex-col">
{@inner_content}
</body>
</html>
"""
end
@spec app(map()) :: Phoenix.LiveView.Rendered.t()
def app(assigns) do
~H"""
{@inner_content}
"""
end
end