defmodule Server.SmartCells.LiveViewNative do
# alias Server.PortAlert
require IEx.Helpers
require Logger
use Kino.JS
use Kino.JS.Live
use Kino.SmartCell, name: "LiveView Native"
@registry_key :liveviews
@impl true
def init(attrs, ctx) do
{:ok,
ctx
|> assign(
path: attrs["path"] || "/",
action: attrs["action"] || ":index"
),
editor: [
attribute: "code",
language: "elixir",
default_source: default_source()
]}
end
@impl true
def handle_connect(ctx) do
{:ok, %{path: ctx.assigns.path, action: ctx.assigns.action}, ctx}
end
@impl true
def to_attrs(ctx) do
%{
"path" => ctx.assigns.path,
"action" => ctx.assigns.action
}
end
@impl true
def to_source(attrs) do
[
# Use our defmodule definition
"""
require Server.Livebook
import Server.Livebook
import Kernel, except: [defmodule: 2]
""",
attrs["code"],
"|> #{register_module_source(attrs)}",
# Restore the existing definition
"""
import Server.Livebook, only: []
import Kernel
:ok
"""
]
end
def register_module_source(attrs) do
quote do
unquote(__MODULE__).register(unquote(attrs["path"]), unquote(attrs["action"]))
end
|> Kino.SmartCell.quoted_to_string()
end
@impl true
def handle_event("update_" <> variable_name, value, ctx) do
variable = String.to_existing_atom(variable_name)
ctx = assign(ctx, [{variable, value}])
broadcast_event(
ctx,
"update_" <> variable_name,
ctx.assigns[variable]
)
{:noreply, ctx}
end
def get_routes() do
Application.get_env(:kino_live_view_native, @registry_key, [])
end
defp put_routes(routes) do
Application.put_env(:kino_live_view_native, @registry_key, routes)
end
def register({:module, module, _, _}, path, action) do
action =
action
|> String.trim_leading(":")
|> String.to_atom()
new_route = %{path: path, module: module, action: action}
get_routes()
# Remove existing route with the same path
|> Enum.reject(fn r -> r.path == path end)
|> Enum.filter(fn r -> is_valid_liveview(r.module) end)
|> Kernel.++([new_route])
|> put_routes()
# Reloading routes must occur before the LV evaluates in Livebook.
# Otherwise the LV will not be avalaible for previously defined routes when changing the LV name.
IEx.Helpers.r(ServerWeb.Router)
Phoenix.PubSub.broadcast!(Server.PubSub, "reloader", :trigger)
end
def default_source() do
~s[defmodule ServerWeb.ExampleLive do
use ServerWeb, :live_view
@impl true
def render(%{format: :swiftui} = assigns) do
~SWIFTUI"""
Hello from LiveView!
""" end end] end asset "main.js" do """ export function init(ctx, payload) { ctx.importCSS("main.css"); ctx.importCSS("https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap"); root.innerHTML = `