ローカルで動くティアランキングボードを作る
調査候補やサービス比較をAIエージェントで進めていると、最終的には「どれを優先するか」を人が見て並べ替える画面がほしくなる。スプレッドシートでも代用できるが、S/A/B/C/Dのような直感的な行、ドラッグ操作、URLからのアイコン取得、ローカル保存をまとめて扱うには少し弱い。
そこで今回は、自分用のティアランキングボードを小さなWebアプリとして実装した。目的は公開サービスを作ることではなく、Hermes Agentの調査結果や候補リストを、手元で安全に分類・見直しできる作業台を作ることだった。
実際に行ったことと確認できた結果
手元の実装では、バックエンドをPythonのFastAPI、保存先をSQLite、フロントエンドをSvelteとViteで構成した。ボードは複数作成でき、既定ではS/A/B/C/Dの行と未分類エリアを持つ。カードはテキスト、URL、画像アップロードに対応し、URLを貼るとページタイトルとアイコンを取得して表示する。
確認日現在、バックエンドの単体・APIテストは `uv run pytest -q` で38件すべて通過した。静的検査も `uv run ruff check backend` と `uv run ruff format –check backend` が成功した。フロントエンドは `npm run check` でSvelteとTypeScriptのエラー・警告が0件だった。さらに `npm run e2e` で本番ビルド後にPlaywrightを起動し、14件通過、実サイト接続を使う1件のみ意図どおりスキップされた。
仕組みまたは処理の流れ
処理の中心は、ローカルAPIとブラウザUIを分けることにした。FastAPI側はボード、Tier行、アイテム、画像、アイコンキャッシュを扱う。SQLiteにはボード構成とカード配置を保存し、画像や取得済みアイコンはローカルのデータディレクトリへ置く。
フロントエンド側は、ボード選択、行の追加・編集、カードのドラッグ、画像貼り付け、トースト通知を担当する。ドラッグ後は配置全体を保存し、リロードしても同じ並びが戻るようにした。URLカードでは、ページHTMLから `og:title`、`title`、ホスト名の順にタイトルを決め、アイコン候補を試す。失敗してもカード作成自体は止めず、ホスト名カードとして残す安全側の設計にした。
再現手順
公開用に一般化すると、手順は次のようになる。
“`bash
cd /path/to/project
uv sync
cd frontend && npm install
cd ..
uv run pytest -q
uv run ruff check backend
uv run ruff format –check backend
cd frontend
npm run check
npm run e2e
“`
利用時は、フロントエンドをビルドしてからFastAPIで配信する。
“`bash
cd /path/to/project/frontend
npm run build
cd ..
uv run rankboard –open
“`
データ保存先を分けたい場合は `RANKBOARD_DATA_DIR=/path/to/data` のように環境変数で指定する。公開記事やチーム共有で使う場合は、実データや内部URLを入れた状態のスクリーンショットを載せない。
失敗しやすい点と安全策
URLからアイコンを取る処理は、相手サイトのHTML、文字コード、favicon指定、ネットワーク状態に左右される。今回の実装では、取得に失敗してもボード操作を止めず、カードは残して通知だけ出すようにした。また、画像アップロードはローカル保存だが、後から公開記事へ転用するなら、個人情報や内部画面が写っていないかを別途確認する必要がある。
もう一つの注意点は、E2Eを「成功した雰囲気」で終わらせないことだ。ビルド、サーバー起動、ブラウザ操作、リロード後の永続化までPlaywrightで見ると、APIだけでは見落とすドラッグ操作や日本語入力の不具合を拾いやすい。
再現チェックリスト
- [ ] ボードを新規作成するとS/A/B/C/D行と未分類が表示される
- [ ] カードを行間で移動し、リロード後も配置が残る
- [ ] URLカードでタイトルまたはホスト名が表示される
- [ ] アイコン取得失敗時もカード作成が止まらない
- [ ] `pytest`、`ruff`、`npm run check`、`npm run e2e` が通る
- [ ] 公開用の説明から絶対パス、内部URL、個人情報を除いている
公式一次情報
- [FastAPI 公式ドキュメント](https://fastapi.tiangolo.com/)
- [SQLite 公式ドキュメント](https://www.sqlite.org/docs.html)
- [Svelte 公式リポジトリ](https://github.com/sveltejs/svelte)
- [Vite 公式リポジトリ](https://github.com/vitejs/vite)
- [Playwright 公式ドキュメント](https://playwright.dev/docs/intro)
