BLE による物理的すれ違いを再現する、位置情報非依存のアバターアプリ。
詳細は docs/要件定義.md を参照。
実装の前に、機能ごとの仕様 (docs/specs/) と契約 (docs/contracts/) を確定させる。
コードはこの仕様への準拠物として扱い、仕様とコードが乖離した場合は 仕様を真実とみなして 修正方針を決める。
- 要件 —
docs/要件定義.md(プロダクト全体の合意) - 仕様 —
docs/specs/<feature>.md(機能単位の WHAT/HOW/受入基準) - 契約 —
docs/contracts/*(型・スキーマ・API。フロントとバックエンドの境界) - 実装 —
src/(Next.js / TypeScript) +src-tauri/(Rust) - 検証 — 受入基準で確認 → 仕様 or 実装を更新
- アプリ: Tauri v2 (macOS デスクトップ + iOS / Android)
- UI: Next.js (App Router) + React + Tailwind CSS + @tanstack/react-query
- コア: Rust (BLE: btleplug @ desktop、CoreBluetooth / Android Bluetooth は Tauri mobile plugin)
- DB ローカル: SQLite (Tauri Plugin SQL)
- DB クラウド: Supabase (Postgres + 匿名 Auth + RLS)
- パッケージマネージャ: pnpm
cp .env.example .env.local
# NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY を埋める
# 未設定でも mock モード (Rust 側 profile_fetch_remote) で動作Supabase 側のスキーマは docs/contracts/supabase-schema.sql を Studio SQL Editor に貼って実行。Authentication > Providers で Anonymous sign-ins を有効化。
サーバーサイドの責務と、将来 Supabase 直結を API サーバーへ置き換える場合の契約は docs/specs/server-side.md と docs/contracts/server-api.md を参照。
ローカル契約検査:
pnpm server:checkサーバー側の実機不要 preflight:
pnpm server:preflightSupabase 実プロジェクトの疎通確認:
NEXT_PUBLIC_SUPABASE_URL=... NEXT_PUBLIC_SUPABASE_ANON_KEY=... pnpm server:smokeserver:smoke は anonymous sign-in、プロフィール upsert / resolve / delete、RLS による他人プロフィール更新拒否、DB 制約による不正プロフィール拒否まで確認する。
pnpm install
pnpm typecheck
(cd src-tauri && cargo check)pnpm dev # ブラウザで http://localhost:1420 (UI のみ、SQLite なし)
pnpm tauri:dev # Tauri WebView (デスクトップアプリ、SQLite + 実 BLE @ macOS)# 初回のみ
pnpm tauri ios init # Xcode プロジェクトを src-tauri/gen/apple に生成
# (cocoapods 必須: brew install cocoapods)
# 開発実行
pnpm tauri ios dev # シミュレータで起動
pnpm tauri ios dev --host # 実機 (要 Apple Developer Team)iOS 環境:
- Xcode 15+ (現状 26.x で確認済)
- rustup target:
aarch64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-ios cocoapods(brew install cocoapods)- 実機ビルドには
tauri.conf.json > bundle > iOS > developmentTeamかAPPLE_DEVELOPMENT_TEAMenv
src-tauri/gen/ は gitignore されるため、tauri ios init のたびに以下のスニペットを 手動マージ してください:
docs/contracts/ios/Info.plist.snippet— Bluetooth 権限文 + 縦固定 + UIBackgroundModesdocs/contracts/ios/project.yml.snippet— CoreBluetooth.framework 依存追加
Android Studio + SDK + NDK が要る。手順:
brew install --cask android-studio # GUI でセットアップ完了させる
# Android Studio > More Actions > SDK Manager で:
# - Android SDK (API 34 以上)
# - Android SDK Platform-Tools
# - NDK (Side by side)
# - Android SDK Command-line Tools
# 環境変数
export ANDROID_HOME="$HOME/Library/Android/sdk"
export NDK_HOME="$ANDROID_HOME/ndk/<version>"
export PATH="$PATH:$ANDROID_HOME/platform-tools"
# Rust target
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
# Tauri Android プロジェクト生成
pnpm tauri android init
pnpm tauri android dev # エミュレータ / 実機で起動| カテゴリ | 状態 |
|---|---|
| UI / 体験 (対面挨拶 / 広場 / プロフィール / ウォークモード) | 全機能実装済み |
| Supabase 連携 (匿名 Auth / RLS / 同意 / 一括 fetch / バックオフ + トースト) | 実装済み |
| ローカル DB (SQLite + migration 0001 / 0002) | 実装済み |
| 実 BLE Scan (btleplug @ desktop) | 実装済み |
| iOS プロジェクト + Bluetooth 権限 + 縦固定 | 生成済み |
| Android Manifest / BLE permission | plugin 側に実装済み |
btleplug は iOS/Android 非対応のため、Mobile では tauri-plugin-encounter-ble
を使う。Android は Service Data + GATT characteristic、iOS は CoreBluetooth の
制約に合わせて Service UUID advertise + GATT read fallback で user_id を交換する。
実装済み:
src-tauri/plugins/tauri-plugin-encounter-bleに iOS Swift / Android Kotlin plugin を追加BleBackend::TauriPluginを追加し、mobile では未指定時に自動採用- native plugin event を既存の encounter 保存フローへ接続
- Android debug APK build は通過済み
- iOS simulator bundle / iPhoneOS
.ipabuild は通過済み
残課題:
- 実機 (iPhone × 2, Android × 1 程度) で相互検出テスト
- iOS 実機インストールには Apple Developer Team / signing 設定が必要
詳細は docs/specs/ble-handshake.md §7 を参照。
実機検証手順は docs/specs/ble-real-device-test.md を参照。
- アバター完全 3D 化 (R3F + glTF)
- LLM ベースの会話生成
- 表情軸 / アクセサリ軸の拡張
- AVATAVI STORE プラットフォーム
- Go 自前バックエンド (Supabase 置換)
- 着せ替え販売
| URL | 用途 |
|---|---|
/encounter-preview |
対面挨拶シーンを iPhone 13 サイズ枠で確認 |
/plaza-preview |
広場ビューを 0/1/8/32/60 人で確認、合流アニメ再生 |
/avatar-preview |
アバターパーツの組み合わせ確認 |
process.env.NEXT_PUBLIC_ENABLE_DEV_PAGES=0 で本番無効化。
# BLE 実装
BLE_BACKEND=mock pnpm tauri:dev # mock peer ループ強制
BLE_BACKEND=btleplug pnpm tauri:dev # btleplug (macOS) 強制
BLE_BACKEND=tauri-plugin pnpm tauri:dev # mobile native plugin 強制
# 未指定は desktop なら btleplug、iOS/Android は tauri-plugin
# Supabase mock モード
# .env.local の URL/ANON_KEY を空にすると Rust の profile_fetch_remote にフォールバックoutput: 'export'の Next.js:pnpm build後は.next/outを削除しないとpnpm devがキャッシュ衝突するtauri-plugin-sqlの permission:capabilities/default.jsonにsql:allow-load/sql:allow-execute/sql:allow-select/sql:allow-closeを明示- Tauri エラーは
string:error instanceof Errorが false になるのでasError(e)でラップ - dev server ポート 1420 固定:
package.jsonのdev: next dev -p 1420とtauri.conf.jsonのdevUrlを一致 - SSR で
Math.random()/Date.now()禁止: hydration mismatch を起こす。useEffect内で生成してsetStateする (例:SakuraPetals)