# Getting Started
Dialupフレームワークを使ったアプリケーションの作成方法。
## 前提条件
- Elixir 1.19以上
## インストール
### 1. ジェネレータのインストール
`mix dialup.new` コマンドをグローバルに使えるよう、まず `dialup_new` をインストール:
```bash
mix archive.install hex dialup_new
```
### 2. 新規プロジェクト作成
```bash
mix dialup.new my_app
cd my_app
mix deps.get
```
これにより、以下のファイルが自動的に生成:
```
my_app/
├── README.md
├── mix.exs # {:dialup, "~> 0.2.0"} を含む
├── .gitignore
├── priv/static/ # 静的ファイル置き場(画像・フォント・favicon など)
└── lib/
├── my_app.ex # Application モジュール
├── root.html.heex # HTML シェル(<head>・hooks・analytics をカスタマイズ)
└── app/
├── layout.ex # Dialup.App.Layout(ルートレイアウト)
├── layout.css # レイアウト共通スタイル(コロケーション CSS)
├── page.ex # Dialup.App.Page(ホームページ)
├── page.css # ホームページ固有スタイル(コロケーション CSS)
├── error.ex # エラーページ(404 / 500)
└── error.css # エラーページスタイル
```
#### ジェネレータのオプション
```bash
# アプリ名をカスタマイズ
mix dialup.new hello --app hello_world
# モジュール名もカスタマイズ
mix dialup.new my_project --app my_project --module MyAwesomeProject
# ヘルプ確認
mix help dialup.new
```
### 2. 起動
```bash
mix run --no-halt
```
ブラウザで `http://localhost:4000` にアクセス。
---
## 手動でのセットアップ(既存プロジェクトへの追加)
既存のMixプロジェクトにDialupを追加する場合:
### 1. dialupの依存追加
`mix.exs` に追加:
```elixir
defp deps do
[
{:dialup, "~> 0.2.0"}
]
end
```
```bash
mix deps.get
```
### 2. アプリケーション設定
`lib/my_app.ex` を作成:
```elixir
defmodule MyApp do
use Application
use Dialup,
app_dir: __DIR__ <> "/app",
title: "My App",
lang: "en"
@impl Application
def start(_type, _args) do
children = [
{Dialup, app: __MODULE__, port: 4000}
]
opts = [
strategy: :one_for_one,
name: __MODULE__.Supervisor
]
Supervisor.start_link(children, opts)
end
end
```
### 3. root.html.heex の作成
`lib/root.html.heex` を作成:
```html
<!DOCTYPE html>
<html lang="{@lang}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{@title}</title>
</head>
<body>
<div id="dialup-root">{raw(@inner_content)}</div>
<script src="/idiomorph.js"></script>
<script src="/dialup.js"></script>
<script>Dialup.connect();</script>
</body>
</html>
```
### 4. 最初のページ作成
`lib/app/page.ex` を作成:
```elixir
defmodule Dialup.App.Page do
use Dialup.Page
def mount(_params, assigns) do
{:ok, Map.put(assigns, :count, 0)}
end
def handle_event("increment", _value, assigns) do
{:patch, "count", render_count(assigns),
Map.update!(assigns, :count, &(&1 + 1))}
end
defp render_count(assigns) do
~H"""
<p id="count">Count: {@count}</p>
"""
end
def render(assigns) do
~H"""
<h1>Hello Dialup</h1>
<%= render_count(assigns) %>
<button ws-event="increment">+</button>
"""
end
end
```
### 5. レイアウトの追加(オプション)
`lib/app/layout.ex` を作成:
```elixir
defmodule Dialup.App.Layout do
use Dialup.Layout
def mount(session) do
{:ok, session}
end
def render(assigns) do
~H"""
<nav>
<a ws-href="/">Home</a>
</nav>
{raw(@inner_content)}
"""
end
end
```
> **注意**: Layout は `#dialup-root` の**中身**のみを生成します。
> `<html>`, `<head>`, `<body>` タグは `lib/root.html.heex` が担当します。
### 6. 起動
```bash
mix run --no-halt
```
## 開発時ホットリロード
開発環境(`Mix.env() == :dev`)では自動的に `Dialup.Reloader` が起動します。`lib/` 以下の `.ex` / `.css` ファイルを保存するたびに:
1. 変更を検出(ポーリング間隔: 500ms)
2. 自動でリコンパイル(`.css` 変更は `@external_resource` 経由で対応する `.ex` も再コンパイル)
3. 既存のWebSocket経由でブラウザを更新(ページリロードなし)
## プロジェクト構成
推奨ディレクトリ構造:
```
my_app/
├── priv/static/ # 静的ファイル(/images/logo.png → /images/logo.png)
└── lib/
├── my_app.ex # Application モジュール(use Dialup)
├── root.html.heex # HTML シェル(<head>・Dialup.connect・analytics)
└── app/
├── layout.ex # Dialup.App.Layout(共通レイアウト)
├── layout.css # 全ページ共通スタイル(オプション)
├── page.ex # Dialup.App.Page(/ ルートページ)
├── page.css # / 固有スタイル(オプション)
├── error.ex # エラーページ(404 / 500)
├── about/
│ └── page.ex # Dialup.App.About(/about)
└── users/
├── layout.ex # Dialup.App.Users.Layout(/users 以下のレイアウト)
├── layout.css # /users 配下全ページのスタイル(オプション)
└── [id]/
└── page.ex # Dialup.App.Users.Id(/users/:id)
```
### root.html.heex
`lib/root.html.heex` は全ページ共通の HTML シェルです。`<head>` タグへの追加、JS ライブラリの読み込み、`Dialup.connect()` のカスタマイズをここで行います。
```html
<!DOCTYPE html>
<html lang="{@lang}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{@title}</title>
<!-- ここに <meta>, <link>, analytics を自由に追加 -->
</head>
<body>
<div id="dialup-root">{raw(@inner_content)}</div>
<script src="/idiomorph.js"></script>
<script src="/dialup.js"></script>
<script>
Dialup.connect({
/* hooks: { MyHook: { mounted(el) {}, destroyed(el) {} } } */
});
</script>
</body>
</html>
```
`id="dialup-root"` と `Dialup.connect()` は必須です。削除すると動作しません。
### 静的ファイル配信
`priv/static/` に配置したファイルは `/` パスから自動配信されます。
```html
<img src="/images/logo.png">
<link rel="icon" href="/favicon.ico">
```
### コロケーション CSS
`page.ex` / `layout.ex` と同名の `.css` ファイルをコロケーション(同ディレクトリに配置)すると、コンパイル時に自動でスコーピングされて注入される。ビルドツール不要。
```css
/* lib/app/page.css */
.hero {
text-align: center;
padding: 4rem 2rem;
}
h1 { font-size: 3rem; }
```
CSS はモジュール名由来の一意なクラス(`d-xxxxxxx`)でラップされ、他ページへの影響を防ぐ。`layout.css` はその layout が包む全子ページに自然にカスケードする。詳細は [Routing](./routing.md#コロケーション-css) を参照。エージェントが「ページごとに CSS が分離されている」と誤解しがちな点は [Agent pitfalls](./agent-pitfalls.md#1-layoutcss-classes-already-apply-on-every-page) を参照。
### 命名規則
| ファイルパス | モジュール名 | URL |
|-------------|-------------|-----|
| `app/page.ex` | `Dialup.App.Page` | `/` |
| `app/layout.ex` | `Dialup.App.Layout` | 全ページ |
| `app/about/page.ex` | `Dialup.App.About.Page` | `/about` |
| `app/users/[id]/page.ex` | `Dialup.App.Users.Id.Page` | `/users/:id` |
| `app/users/layout.ex` | `Dialup.App.Users.Layout` | `/users/*` |
## 基本的な動作確認
ボタンをクリックしてカウントが増えれば成功。ページ遷移はSPAとして動作し、WebSocket経由でサーバーと通信しています。
## Plug ミドルウェア
`use Dialup` に `plugs:` オプションを指定することで、HTTP リクエスト処理のパイプラインにカスタム Plug を追加できる。認証、CORS、ロギングなどに使用する。
```elixir
defmodule MyApp do
use Application
use Dialup,
app_dir: __DIR__ <> "/app",
title: "My App",
plugs: [
MyApp.AuthPlug,
{Corsica, origins: "*"}
]
# ...
end
```
Plug は指定した順序で実行される。Plug が `conn` を halt した場合、後続の Plug と Dialup の処理はスキップされる。
各 Plug は標準の `Plug` ビヘイビア(`init/1` と `call/2`)を実装する必要がある。
## 認証(オプション)
ユーザー認証が必要なアプリでは `mix dialup.gen.auth` で Ecto ベースの scaffold を生成し、
`auth_accounts` と `Dialup.Auth.Plug` を Application に配線する。
```bash
mix dialup.gen.auth Accounts User users
```
```elixir
use Dialup,
app_dir: __DIR__ <> "/app",
auth_accounts: MyApp.Accounts,
plugs: [{Dialup.Auth.Plug, accounts: MyApp.Accounts}]
```
HTTP ログイン・MCP 権限・CSRF・セッション同期の詳細は [Authentication](./authentication.md) を参照。
## 次のステップ
- [Routing](./routing.md) - URLとページの対応関係 / コロケーション CSS / エラーページ
- [Lifecycle](./lifecycle.md) - mount/render/handle_event/page_title の流れ
- [Authentication](./authentication.md) - `mix dialup.gen.auth` と MCP 権限連携
- [Testing](./testing.md) - ページのユニットテスト
- [Telemetry](./telemetry.md) - 計測とモニタリング