Livebook から Phoenix LiveView の画面を実装する

## はじめに

KinoLiveView を使うと Livebook から Phoenix LiveView の画面が実装できます

https://github.com/brooklinjazz/kino_live_view

## 準備

### Phoenix LiveView

新しい Phoenix LiveView のプロジェクト `app` を作成します

データベースを使わない場合、 `--no-ecto` で Ecto なしのプロジェクトが作成できます

```bash
mix phx.new app --no-ecto
```

作成されたディレクトリーに移動します

```bash
cd app
```

以下の通り各ファイルを編集します

- mix.exs

  依存モジュールに KinoLiveView を追加します

  ```elixir
  def deps do
    [
      ...
      {:kino_live_view, "~> 0.1.0", only: :dev},
      ...
    ]
  end
  ```

- config/dev.exs

  末尾に以下の設定を追加します

  ```elixir
  ...

  config :kino_live_view,
    enabled: true,
    pubsub_server: App.PubSub,
    router_module: AppWeb.Router,
    socket: KinoLiveView.LiveReloader.Socket
  ```

- lib/app_web/endpoint.ex

  Livebook で Evaluate したときにリロードされるように設定します

  ```elixir
    ...
    if code_reloading? do
      socket "/phoenix/live_reload/socket", Application.get_env(:kino_live_view, :socket, Phoenix.LiveReloader.Socket)
      plug Phoenix.LiveReloader
      plug Phoenix.CodeReloader
      plug Phoenix.Ecto.CheckRepoStatus, otp_app: :app
    end
  end
  ```

- lib/app_web/router.ex

  Livebook で指定したルートを動的に追加できるようにします

  ```elixir
    ...
    if Application.compile_env(:kino_live_view, :enabled) do
      scope "/" do
        pipe_through :browser

        KinoLiveView.get_routes()
        |> Enum.map(fn %{path: path, module: module, action: action} ->
          live(path, module, action)
        end)
      end
    end
  end
  ```

- lib/app/application.ex

  Livebook からスマートセルが使えるようにします

  ```elixir
    ...
    @impl true
    def start(_type, _args) do
      children = [
        ...
      ]

      if Application.get_env(:kino_live_view, :enabled) do
        Kino.SmartCell.register(KinoLiveView.SmartCell)
      end
      ...
      opts = [strategy: :one_for_one, name: App.Supervisor]
      Supervisor.start_link(children, opts)
    end
    ...
  ```
  
各ファイルの編集が完了したら依存モジュールをインストールします

```bash
mix deps.get
```

以下のコマンドで Phoenix を起動します

```bash
iex --sname test --cookie mycookie -S mix phx.server
```

### Livebook

Phoenix LiveView とは別のディレクトリーに Livebook のソースコードを取得します

```bash
git clone https://github.com/livebook-dev/livebook.git \
  && cd livebook
```

依存モジュールをインストールします

```bash
mix deps.get
```

Phoenix と重複しないよう、起動するポートを変更します

config/dev.exs
```diff_elixir
...
config :livebook, LivebookWeb.Endpoint,
  # Binding to loopback ipv4 address prevents access from other machines.
  # Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
  http: [
    ip: {127, 0, 0, 1},
-   port: 4000,
+   port: 4040,
    http_1_options: [max_header_length: 32768],
    http_2_options: [max_header_value_length: 32768]
  ],
  ...
```

Livebook を起動します

```bash
mix phx.server
```

ブラウザから <http://localhost:4040> を開きます

## Livebook からの接続

新しいノートブックを起動し、左メニューの CPU アイコンをクリックします

RUNTIME メニューが開くので、歯車アイコン（下画像の赤枠）をクリックします

![スクリーンショット 2023-12-25 21.44.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/edf9a8cb-c284-fc95-959f-8dd64dfb2aa9.png)

Runtime settings モーダルが開くので、 Name に test 、 Cookie に mycookie （Phoenix LiveView 起動時に指定した値）を入力します

Connect ボタンをクリックしてからモーダルを閉じます

![スクリーンショット 2023-12-25 21.48.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/fda3d9a7-98cc-7160-4f14-12eea835168a.png)

Phoenix LiveView に接続されました

## Web ページの追加

スマートセルの追加候補が KinoLiveView だけになっています

![スクリーンショット 2023-12-25 21.50.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/169e5fa5-7a04-de09-434a-bebb660009c3.png)

スマートセルを追加すると、以下のような表示になります

![スクリーンショット 2023-12-25 21.53.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/250e142c-8cef-5570-f364-c426b59a0545.png)

ROUTE に `/livebook` を入力し、セルに以下のように入力します

```elixir
defmodule AppWeb.LivebookLive.Index do
  use AppWeb, :live_view

  @impl true
  def render(assigns) do
    ~H"""
    <div>
      Hello Livebook!!
    </div>
    """
  end
end
```

別のウィンドウで <http://localhost:4000/livebook> にアクセスすると、セルに書いた内容が Web ページとして表示されています

![スクリーンショット 2023-12-25 21.58.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/5dad6472-2c36-9583-9d95-5e0b26525606.png)

セルの内容を書き換えて Reevaluate をクリックすると、 Phoenix LiveView がリロードされ、変更が反映されます

![kino_live_view.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/9f45b3a5-5ef6-111e-f216-c57ec44b9a8e.gif)

## 注意点

例えばセルの中身を以下のようにしたとします

```elixir
defmodule AppWeb.LivebookLive.Index do
  use AppWeb, :live_view

  @impl true
  def render(assigns) do
    ~H"""
    <div class="bg-red-100">
      Hello Livebook!?
    </div>
    """
  end
end
```

`class="bg-red-100"` によって背景色が赤色に変わりそうですが、これは反映されません

TailwindCSS の `bg-red-100` クラスを元の Phoenix LiveView のプロジェクト内で使用していないため、 CSS が生成されないためです

反映させたい場合、Phoenix LiveView のプロジェクト内のどこかに同じクラス名を入れておく必要があります

## まとめ

KinoLiveView は README 冒頭にある通り、実験的なプロジェクトです

> This project is experimental.

現時点ではあまり実用性がなさそうですが、今後、 Livebook からスタイルも含め自由に画面が組めるようになれば楽しいですね