指示と環境

指示

# 今回のタスク
- メモを貼り付けられるWebホワイトボードを作成する
- ユーザーはブラウザを開くだけでホワイトボードにアクセスできる
- ユーザーはホワイトボードにメモを貼り付けることができる
- ユーザーはホワイトボードに貼り付けたメモを移動させることができる
- ユーザーはホワイトボードに貼り付けたメモを削除することができる
- ユーザーはホワイトボードに貼り付けたメモを編集することができる
- ユーザーはホワイトボードに貼り付けたメモを保存することができる
- ユーザーはホワイトボードに貼り付けたメモを保存した状態でブラウザを閉じることができる
- ユーザーはホワイトボードに貼り付けたメモを保存した状態でブラウザを再度開くことができる

# 前提
- このプロジェクトでは vibeコーディングを中心に進めるので極力シンプルな構成で開発を行う

# 技術スタック
- vanilla JS
- htmx
- CSS
- uv
- python (server)
- sqllite

環境

  • VSCode
  • Cline
  • gemini-2.5-pro

ソース

出来上がったもの

Python の Web フレームワークである Flask と、HTML を拡張して動的な UI を実現するライブラリ htmx を用いて、シンプルな Web ホワイトボードアプリケーション「vibe-htmx」を構築するプロセスを紹介します。バックエンドには Flask と SQLite、フロントエンドには htmx と Vanilla JavaScript を採用し、パッケージ管理には高速なツール uv を使用します。

アプリケーション概要

「vibe-htmx」は、ユーザーがブラウザ上でメモを自由に追加、表示、移動、編集、削除できる仮想的なホワイトボードです。作成されたメモとその位置情報はサーバーサイドの SQLite データベースに保存され、永続化されます。

主な機能は以下の通りです。

  • メモの追加
  • メモの一覧表示
  • メモのドラッグ&ドロップによる位置変更 (変更は自動保存)
  • メモ内容のインライン編集 (変更は自動保存)
  • メモの削除

技術スタック

本プロジェクトで使用する主要な技術は以下の通りです。

  • バックエンド:
    • Python 3.13+
    • Flask 3+ (Web フレームワーク)
    • SQLite (組み込みデータベース)
  • フロントエンド:
    • htmx 1.9+ (HTML 属性による AJAX リクエスト)
    • Vanilla JavaScript (ドラッグ&ドロップ、インライン編集など htmx を補完)
    • CSS3 (スタイリング)
  • パッケージ管理:
    • uv (Python パッケージインストーラー兼仮想環境マネージャー)

uv を採用することで、pipvenv の機能を統一的に、かつ高速に扱うことを目指します。

プロジェクト構成

プロジェクトの主要なファイル構成は以下のようになります。

vibe-htmx/
├── .python-version       # pyenv 用 Python バージョン指定 (uv 環境でも参考になる)
├── .rule                 # プロジェクト固有の開発ルール
├── README.md             # プロジェクト概要、セットアップ手順
├── cline_docs/           # (Cline 用ドキュメント)
├── instance/
│   └── database.db       # SQLite データベースファイル
├── main.py               # Flask アプリケーション本体 (バックエンドロジック)
├── pyproject.toml        # プロジェクト設定、依存関係定義 (uv が参照)
├── schema.sql            # データベーススキーマ定義
├── static/               # 静的ファイル (CSS, JavaScript, htmx ライブラリ)
│   ├── htmx.min.js
│   ├── script.js
│   └── style.css
├── templates/
│   └── index.html        # アプリケーションの基本 HTML 構造
└── uv.lock               # 依存関係のロックファイル (uv が生成・使用)

セットアップ手順

アプリケーションを実行するためのセットアップ手順です。

  1. リポジトリのクローン:

    git clone <repository-url>
    cd vibe-htmx
    
  2. Python 環境の準備: このプロジェクトは Python 3.13 を想定しています。uvpyproject.tomlrequires-python を参照しますが、システムに適切な Python バージョンがインストールされている必要があります。必要に応じて pyenv などで Python 3.13 をインストールしてください。

  3. 依存関係のインストール: uv を使用して、pyproject.toml に定義された依存関係を仮想環境にインストール・同期します。

    uv sync
    

    これにより、プロジェクト用の仮想環境 (.venv) が作成され、Flask などのライブラリがインストールされます。

  4. Python バージョンの確認 (任意): uv が管理する Python のバージョンを確認するには、次のコマンドを実行します。

    uv run python -V
    
  5. データベースの初期化: schema.sql に基づいて SQLite データベース (instance/database.db) を初期化します。Flask の CLI コマンドとして実装されています。

    uv run flask --app main init-db
    

バックエンド実装 (Flask: main.py)

バックエンドは Flask を使用し、API エンドポイントを提供します。

主要な機能

  • データベース接続: リクエストごとに SQLite データベースへの接続を管理します (get_db, close_connection)。
  • データベース初期化: flask init-db コマンドで schema.sql を実行します (init_db, init_db_command)。
  • HTML フラグメント生成: _generate_note_html 関数で、単一メモの HTML 表現を動的に生成します。これは htmx がレスポンスとして受け取り、DOM に挿入します。
  • ルート (/): templates/index.html を配信します。
  • API エンドポイント:
    • GET /api/notes: 全てのメモを取得し、HTML フラグメントのリストとして返します。htmx はこれを初期表示や更新時に利用します。
    • POST /api/notes: 新しいメモを作成し、作成されたメモの HTML フラグメントを返します。htmx はこれをリストに追加します。
    • PUT /api/notes/<int:note_id>/position: メモの位置 (x, y 座標) を更新します。成功時は 204 No Content を返します。
    • PUT /api/notes/<int:note_id>: メモの内容を更新し、更新されたメモの HTML フラグメントを返します。
    • DELETE /api/notes/<int:note_id>: 指定されたメモを削除します。成功時は空のレスポンス (200 OK) を返します。htmx はこれを受け取り、対応する要素を DOM から削除します。

htmx との連携

Flask の各 API エンドポイントは、htmx が期待する形式でレスポンスを返すように設計されています。特に、メモの追加や更新時には、操作対象となったメモに対応する HTML フラグメントを返すことで、htmx がスムーズに部分的な DOM 更新を行えるようにしています。

フロントエンド実装 (htmx + Vanilla JS)

フロントエンドは templates/index.html を基本構造とし、htmx と Vanilla JavaScript を組み合わせて動的な挙動を実現します。

templates/index.html (骨格)

  • htmx ライブラリ (htmx.min.js) とカスタム JavaScript (script.js)、CSS (style.css) を読み込みます。
  • メモを追加するためのフォームを配置します (POST /api/notes をトリガー)。
  • メモを表示するためのコンテナ (<div id="notes-container"> など) を用意します。このコンテナに GET /api/notes から取得したメモの HTML フラグメントが初期表示されます。

htmx の役割

  • 初期表示: ページロード時に hx-get="/api/notes"hx-trigger="load" を使ってメモ一覧を取得し、コンテナに表示します。
  • メモ追加: フォームの送信時に hx-post="/api/notes" を使用し、サーバーからのレスポンス (新しいメモの HTML) をコンテナに追加します (hx-swap="beforeend" など)。
  • メモ削除: 削除ボタンに hx-delete="/api/notes/{note_id}" を設定し、レスポンスに応じて対象要素を削除します (hx-target="closest .note", hx-swap="outerHTML" など)。
  • 編集トリガー (一部): ダブルクリックイベントを捕捉し、編集用の UI (例: <textarea>) をサーバーから取得またはクライアントサイドで表示する起点として利用できます (今回の実装では JS で処理)。
  • 位置・内容更新: JavaScript でドラッグや編集が完了したタイミングで、hx-put を含むリクエストを JavaScript から htmx の API (htmx.ajax) を使って送信するか、隠しフォームなどを用いて送信します。

Vanilla JavaScript (static/script.js) の役割

htmx だけでは実装が複雑になる、または適していないインタラクションを JavaScript で補完します。

  • ドラッグ&ドロップ: メモ要素をマウスで掴んで移動させる機能。移動完了時にメモの ID と新しい座標 (x, y) を取得し、PUT /api/notes/{note_id}/position エンドポイントに送信します。
  • インライン編集: メモの内容部分をダブルクリックした際に、表示用の <div> を編集可能な <textarea> に置き換えます。編集完了 (フォーカスが外れる、Enter キー押下など) 時に、新しい内容を PUT /api/notes/{note_id} エンドポイントに送信します。サーバーからのレスポンス (更新されたメモの HTML) を受け取り、<textarea> を元の表示形式に戻します。

データベース設計 (schema.sql)

データベースは SQLite を使用し、notes テーブルのみのシンプルな構造です。

DROP TABLE IF EXISTS notes;

CREATE TABLE notes (
  id INTEGER PRIMARY KEY AUTOINCREMENT, -- メモの一意な ID (自動採番)
  content TEXT NOT NULL,             -- メモの内容
  pos_x INTEGER NOT NULL DEFAULT 0,    -- メモの X 座標
  pos_y INTEGER NOT NULL DEFAULT 0     -- メモの Y 座標
);

開発環境の整備 (uv ルールの徹底)

本プロジェクトでは、Python のパッケージ管理とコマンド実行に uv を全面的に採用しています。開発の一貫性を保つため、.rule ファイルに以下のルールを明記しています。

# 開発ルール
- **Python関連のコマンド (スクリプト実行、Flaskサーバー起動など) は、OS標準の `python` や `pyenv` 経由ではなく、必ず `uv run` を使用する。**
  - 例: `uv run python your_script.py`
  - 例: `uv run flask --app main run`
- 依存関係の管理は `uv sync` を使用する。

これにより、開発者は常に uv が管理する仮想環境下でコマンドを実行することが保証され、環境差異による問題を低減できます。README.md のセットアップ手順も、このルールに基づき uv run を使用するように修正されています。

まとめ

Flask, htmx, Vanilla JavaScript, SQLite, そして uv を組み合わせることで、比較的少ないコード量でインタラクティブな Web アプリケーションを構築できることを示しました。htmx がサーバーサイドレンダリングされた HTML を活用して AJAX 通信を簡素化し、JavaScript はより複雑な UI 操作に集中するという役割分担が効果的です。また、uv を用いた開発ルールの標準化は、プロジェクトの保守性向上に寄与します。

本記事が、同様の技術スタックを用いたアプリケーション開発の参考となれば幸いです。