---
title: "クイックスタート — zenit Zig UI ドキュメント"
description: "zenit リポジトリの外に置き、単独でビルド・実行できるネイティブ macOS アプリを作成します。"
url: https://zenit.z.express/ja/docs/guide/getting-started
language: ja
alternate_en: https://zenit.z.express/docs/guide/getting-started.md
alternate_zh: https://zenit.z.express/zh/docs/guide/getting-started.md
alternate_es: https://zenit.z.express/es/docs/guide/getting-started.md
alternate_ko: https://zenit.z.express/ko/docs/guide/getting-started.md
alternate_fr: https://zenit.z.express/fr/docs/guide/getting-started.md
alternate_de: https://zenit.z.express/de/docs/guide/getting-started.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# クイックスタート

zenit リポジトリの外に置き、単独でビルド・実行できるネイティブ macOS アプリを作成します。

Claude Code や他の Agent で開発していますか？ まず zenit UI dev Skill を入れましょう。

この手引き、コンポーネント選定表、実プロジェクトで踏んだ落とし穴を、Agent が必要に応じて読み込める指示としてまとめています。書かれるコードはそのまま zenit の規約に沿います。

[Skill をダウンロード.zip · 54 KB](https://zenit.z.express/downloads/zenit-ui-dev.zip)[インストールガイド](https://zenit.z.express/ja/docs/guide/ai-skill)

## 前提条件

このリリースは macOS を対象とし、Apple Silicon で継続的に検証しています。Zig 0.15.2 と Xcode Command Line Tools があることを確認してください。Bun はエンドツーエンドテストを実行する場合にのみ必要です。

```sh
zig version
xcode-select -p
```

> WARNING
> 
> **バージョンは一致させてください。** テンプレートは `build.zig.zon` で `minimum_zig_version = "0.15.2"` を宣言しています。他のバージョンではビルド API やパッケージのフィンガープリントで分かりにくいエラーが起きることがあります。

## プロジェクトを作成

リポジトリの `templates/minimal-app` から始めます。これは完全な下流プロジェクトです：

`templates/minimal-app`

```
minimal-app/
├── README.md
├── build.zig          # zenit.attach() + run / app steps
├── build.zig.zon      # declares the zenit dependency
├── e2e/
│   └── record-demo.ts # drives and records the app through the Harness
└── src/
    └── main.zig       # counter-button app
```

1.  **テンプレートをコピー**
    
    templates/minimal-app を zenit リポジトリ外の新しいディレクトリにコピーします。
    
2.  **依存パスを修正**
    
    テンプレートの既定値 `.path = "../../"` はリポジトリ内でしか解決できません。コピー後、build.zig.zon からの相対パスで zenit の checkout を指すよう変更するか、`zig fetch --save=zenit <url>` で公開リビジョンに固定してください。
    
3.  **フィンガープリントを生成**
    
    テンプレートの .fingerprint を流用しないでください。共有した 2 つのプロジェクトが Zig のパッケージキャッシュで衝突します。その行を削除して zig build を 1 回実行し、エラーに表示された値を貼り戻します。ついでに .name = .myapp も変更してください。
    

```sh
git clone https://github.com/version-next/zenit.git
cp -R zenit/templates/minimal-app ./myapp
cd myapp   # then set .zenit = .{ .path = "../zenit" } in build.zig.zon
zig build
```

## zenit を接続

`build.zig.zon` が依存を宣言し、`build.zig` は `@import("zenit")` で zenit 自身のビルド API を取得します。

`build.zig.zon`

```zig
.{
    .name = .myapp,
    .version = "0.1.0",
    .fingerprint = 0x8798022a7f8e220e, // replace: delete this line, run zig build once
    .minimum_zig_version = "0.15.2",

    .dependencies = .{
        // Relative to this file. Absolute paths are rejected by Zig 0.15.2.
        .zenit = .{ .path = "../zenit" },
    },

    .paths = .{
        "README.md",
        "build.zig",
        "build.zig.zon",
        "e2e",
        "src",
    },
}
```

以下はテンプレートの build.zig そのままです。attach 1 行だけではなく、テスト用スイッチの転送、Harness クライアントのインストール、run ステップと .app パッケージングも含みます：

`build.zig`

```zig
const std = @import("std");
const zenit = @import("zenit");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const test_mode = b.option(bool, "test-mode", "Enable the Zenit automation harness") orelse false;
    const e2e_port = b.option(u16, "e2e-port", "Zenit Harness RPC directory suffix") orelse 19816;

    const zenit_dep = b.dependency("zenit", .{
        .target = target,
        .optimize = optimize,
        // Dependency build options are isolated in Zig. Forward these
        // explicitly so `zig build -Dtest-mode=true` reaches Zenit.
        .@"test-mode" = test_mode,
        .@"e2e-port" = e2e_port,
    });

    const exe = b.addExecutable(.{
        .name = "myapp",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        }),
    });

    zenit.attach(zenit_dep, exe);
    zenit.installHarnessClient(b, zenit_dep);

    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    // Optional: `zig build app` packages a double-clickable .app bundle.
    if (target.result.os.tag == .macos) {
        const bundled = zenit.bundleApp(b, .{
            .exe = exe,
            .display_name = "My App",
            .bundle_id = "com.example.myapp",
            .version = "0.1.0",
            .signing = .ad_hoc,
        });
        const app_step = b.step("app", "Build the .app bundle");
        app_step.dependOn(bundled.final_step);
    }
}
```

| 呼び出し | 役割 |
| --- | --- |
| `zenit.attach(zenit_dep, exe)` | exe に ui と zenit\_app の 2 モジュールの import を追加し、macOS の ObjC ブリッジ 5 つをコンパイルして、Cocoa・Metal・CoreText などのフレームワークをリンク |
| `.@"test-mode" / .@"e2e-port"` | Zig の依存オプションは分離されているため、明示的に転送しないと zig build -Dtest-mode=true が zenit に届かない |
| `zenit.installHarnessClient(b, zenit_dep)` | 型付きの Harness クライアントを zig-out/share/zenit/harness/client.ts にインストールし、e2e/record-demo.ts から使う |
| `zenit.bundleApp(b, .{ ... })` | ダブルクリックで起動できる zig-out/<display\_name>.app を生成。ここでは ad-hoc 署名 |

## 最初の UI ツリーをマウント

`App.runWith` はルート Scope を作成し、マウント関数を 1 回呼び出して、イベントループを引き継ぎます。マウント関数のシグネチャは `fn (cx: *ui.Cx, scope: *ui.Scope) anyerror!*ui.Node` でなければならず、アプリはルートノードを返すだけです。

`src/main.zig`

```zig
const std = @import("std");
const ui = @import("ui");
const App = @import("zenit_app").App;

const Counter = struct {
    n: u32 = 0,
    label: ?*ui.Node = null,
    buf: [32]u8 = undefined,

    pub fn increment(self: *Counter) void {
        self.n += 1;
        const node = self.label orelse return;
        const content = std.fmt.bufPrint(&self.buf, "Clicked {d} times", .{self.n}) catch return;
        if (node.getText()) |old| {
            var t = old;
            t.content = content;
            node.setText(t);
        }
        node.markRenderDirty();
    }
};

fn mountUi(cx: *ui.Cx, scope: *ui.Scope) anyerror!*ui.Node {
    const allocator = cx.allocator;

    const counter = try cx.bindState(Counter, .{});
    const click = cx.on(Counter, counter, Counter.increment);

    const root = try ui.box(cx, .{
        .width = .fill(),
        .height = .fill(),
        .direction = .column,
        .gap = 16,
        .padding = ui.Padding.all(40),
        .background = cx.tokens.color.bg_primary,
        .align_items = .center,
        .justify = .center,
    }, .{});

    try root.appendChild(allocator, try ui.text(cx, "Hello, zenit!", .{
        .font_size = 24,
        .font_weight = 600,
        .color = cx.tokens.color.fg_primary,
    }));

    const button = try ui.widgets.Button(.{
        .label = "Click me",
        .variant = .primary,
        .on_click = click,
    }).mount(scope, cx);
    button.meta.ownership.meta.test_id = "counter.increment";
    try root.appendChild(allocator, button);

    const label = try ui.text(cx, "Clicked 0 times", .{
        .font_size = 14,
        .color = cx.tokens.color.fg_secondary,
    });
    try root.appendChild(allocator, label);
    counter.label = label;

    return root;
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    const app = try App.init(gpa.allocator(), .{
        .window = .{ .width = 640, .height = 480, .title = "Hello, zenit" },
    });
    defer app.deinit();

    try app.runWith(mountUi);
}
```

`cx.bindState` は `Counter` を Cx に持たせ、`cx.on` はそのメソッドをボタンのコールバックに変えます。`test_id` により E2E スクリプトが意味でボタンを見つけられます。

> NOTE
> 
> **テンプレートは最小構成のため手動でテキストを更新します。** バッファは `Counter` 内の安定したアドレスにあるため、`setText` に貸しても安全です。実際のアプリでは、カウントを Signal に置き、`ui.textFmt` にラベルを更新させるのが一般的です。[リアクティビティ](https://zenit.z.express/ja/docs/guide/reactivity)を参照してください。

## ビルドと実行

```sh
zig build run   # build and run the unbundled executable
zig build app   # package zig-out/My App.app (macOS)
```

[Video](https://zenit.z.express/media/hello-button.mp4?v=b4d2b8f8a0)

ビルド成果物から起動した実際の Hello Button.app（examples/hello\_button、テンプレートと同じカウンターボタン）：harness が hand カーソルで 3 回クリックし、ラベルが「Clicked 3 times」になります。ウィンドウ、ウィジェット、応答はすべて上記の公開 API によるものです。

テンプレートには録画スクリプトも付属しています。`zig build -Dtest-mode=true` でビルドしてアプリを起動し、`e2e/record-demo.ts` を実行すると同様の動画が得られます。詳しくは [E2E harness](https://zenit.z.express/ja/docs/advanced/e2e) を参照してください。

> TIP
> 
> **次のステップ。** ウィンドウが表示されたら、次にプロジェクト構成と UI ツリーを読んでください。独自のコンポーネント層を急いで作る必要はありません。zenit にはウィジェット、テーマ、リアクティブなプリミティブがすでにあります。
